mirror of
https://github.com/juanfont/headscale.git
synced 2024-12-22 07:57:34 +00:00
Lock and unify headscale start/get method
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
parent
4799859be0
commit
93d56362af
@ -77,12 +77,12 @@ func TestAuthWebFlowAuthenticationPingAll(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *AuthWebFlowScenario) CreateHeadscaleEnv(namespaces map[string]int) error {
|
func (s *AuthWebFlowScenario) CreateHeadscaleEnv(namespaces map[string]int) error {
|
||||||
err := s.StartHeadscale()
|
headscale, err := s.Headscale()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.MustHeadscale().WaitForReady()
|
err = headscale.WaitForReady()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -99,7 +99,7 @@ func (s *AuthWebFlowScenario) CreateHeadscaleEnv(namespaces map[string]int) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.runTailscaleUp(namespaceName, s.MustHeadscale().GetEndpoint())
|
err = s.runTailscaleUp(namespaceName, headscale.GetEndpoint())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -145,8 +145,13 @@ func (s *AuthWebFlowScenario) runTailscaleUp(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *AuthWebFlowScenario) runHeadscaleRegister(namespaceStr string, loginURL *url.URL) error {
|
func (s *AuthWebFlowScenario) runHeadscaleRegister(namespaceStr string, loginURL *url.URL) error {
|
||||||
|
headscale, err := s.Headscale()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("loginURL: %s", loginURL)
|
log.Printf("loginURL: %s", loginURL)
|
||||||
loginURL.Host = fmt.Sprintf("%s:8080", s.MustHeadscale().GetIP())
|
loginURL.Host = fmt.Sprintf("%s:8080", headscale.GetIP())
|
||||||
loginURL.Scheme = "http"
|
loginURL.Scheme = "http"
|
||||||
|
|
||||||
httpClient := &http.Client{}
|
httpClient := &http.Client{}
|
||||||
@ -177,7 +182,7 @@ func (s *AuthWebFlowScenario) runHeadscaleRegister(namespaceStr string, loginURL
|
|||||||
key := keySep[1]
|
key := keySep[1]
|
||||||
log.Printf("registering node %s", key)
|
log.Printf("registering node %s", key)
|
||||||
|
|
||||||
if headscale, ok := s.controlServers["headscale"]; ok {
|
if headscale, err := s.Headscale(); err == nil {
|
||||||
_, err = headscale.Execute(
|
_, err = headscale.Execute(
|
||||||
[]string{"headscale", "-n", namespaceStr, "nodes", "register", "--key", key},
|
[]string{"headscale", "-n", namespaceStr, "nodes", "register", "--key", key},
|
||||||
)
|
)
|
||||||
|
@ -39,8 +39,11 @@ func TestNamespaceCommand(t *testing.T) {
|
|||||||
err = scenario.CreateHeadscaleEnv(spec)
|
err = scenario.CreateHeadscaleEnv(spec)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
headscale, err := scenario.Headscale()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var listNamespaces []v1.Namespace
|
var listNamespaces []v1.Namespace
|
||||||
err = executeAndUnmarshal(scenario.MustHeadscale(),
|
err = executeAndUnmarshal(headscale,
|
||||||
[]string{
|
[]string{
|
||||||
"headscale",
|
"headscale",
|
||||||
"namespaces",
|
"namespaces",
|
||||||
@ -61,7 +64,7 @@ func TestNamespaceCommand(t *testing.T) {
|
|||||||
result,
|
result,
|
||||||
)
|
)
|
||||||
|
|
||||||
_, err = scenario.MustHeadscale().Execute(
|
_, err = headscale.Execute(
|
||||||
[]string{
|
[]string{
|
||||||
"headscale",
|
"headscale",
|
||||||
"namespaces",
|
"namespaces",
|
||||||
@ -75,7 +78,7 @@ func TestNamespaceCommand(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var listAfterRenameNamespaces []v1.Namespace
|
var listAfterRenameNamespaces []v1.Namespace
|
||||||
err = executeAndUnmarshal(scenario.MustHeadscale(),
|
err = executeAndUnmarshal(headscale,
|
||||||
[]string{
|
[]string{
|
||||||
"headscale",
|
"headscale",
|
||||||
"namespaces",
|
"namespaces",
|
||||||
@ -117,13 +120,16 @@ func TestPreAuthKeyCommand(t *testing.T) {
|
|||||||
err = scenario.CreateHeadscaleEnv(spec)
|
err = scenario.CreateHeadscaleEnv(spec)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
headscale, err := scenario.Headscale()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
keys := make([]*v1.PreAuthKey, count)
|
keys := make([]*v1.PreAuthKey, count)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
for index := 0; index < count; index++ {
|
for index := 0; index < count; index++ {
|
||||||
var preAuthKey v1.PreAuthKey
|
var preAuthKey v1.PreAuthKey
|
||||||
err := executeAndUnmarshal(
|
err := executeAndUnmarshal(
|
||||||
scenario.MustHeadscale(),
|
headscale,
|
||||||
[]string{
|
[]string{
|
||||||
"headscale",
|
"headscale",
|
||||||
"preauthkeys",
|
"preauthkeys",
|
||||||
@ -149,7 +155,7 @@ func TestPreAuthKeyCommand(t *testing.T) {
|
|||||||
|
|
||||||
var listedPreAuthKeys []v1.PreAuthKey
|
var listedPreAuthKeys []v1.PreAuthKey
|
||||||
err = executeAndUnmarshal(
|
err = executeAndUnmarshal(
|
||||||
scenario.MustHeadscale(),
|
headscale,
|
||||||
[]string{
|
[]string{
|
||||||
"headscale",
|
"headscale",
|
||||||
"preauthkeys",
|
"preauthkeys",
|
||||||
@ -202,7 +208,7 @@ func TestPreAuthKeyCommand(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Test key expiry
|
// Test key expiry
|
||||||
_, err = scenario.MustHeadscale().Execute(
|
_, err = headscale.Execute(
|
||||||
[]string{
|
[]string{
|
||||||
"headscale",
|
"headscale",
|
||||||
"preauthkeys",
|
"preauthkeys",
|
||||||
@ -216,7 +222,7 @@ func TestPreAuthKeyCommand(t *testing.T) {
|
|||||||
|
|
||||||
var listedPreAuthKeysAfterExpire []v1.PreAuthKey
|
var listedPreAuthKeysAfterExpire []v1.PreAuthKey
|
||||||
err = executeAndUnmarshal(
|
err = executeAndUnmarshal(
|
||||||
scenario.MustHeadscale(),
|
headscale,
|
||||||
[]string{
|
[]string{
|
||||||
"headscale",
|
"headscale",
|
||||||
"preauthkeys",
|
"preauthkeys",
|
||||||
@ -254,9 +260,12 @@ func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) {
|
|||||||
err = scenario.CreateHeadscaleEnv(spec)
|
err = scenario.CreateHeadscaleEnv(spec)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
headscale, err := scenario.Headscale()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var preAuthKey v1.PreAuthKey
|
var preAuthKey v1.PreAuthKey
|
||||||
err = executeAndUnmarshal(
|
err = executeAndUnmarshal(
|
||||||
scenario.MustHeadscale(),
|
headscale,
|
||||||
[]string{
|
[]string{
|
||||||
"headscale",
|
"headscale",
|
||||||
"preauthkeys",
|
"preauthkeys",
|
||||||
@ -273,7 +282,7 @@ func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) {
|
|||||||
|
|
||||||
var listedPreAuthKeys []v1.PreAuthKey
|
var listedPreAuthKeys []v1.PreAuthKey
|
||||||
err = executeAndUnmarshal(
|
err = executeAndUnmarshal(
|
||||||
scenario.MustHeadscale(),
|
headscale,
|
||||||
[]string{
|
[]string{
|
||||||
"headscale",
|
"headscale",
|
||||||
"preauthkeys",
|
"preauthkeys",
|
||||||
@ -316,9 +325,12 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) {
|
|||||||
err = scenario.CreateHeadscaleEnv(spec)
|
err = scenario.CreateHeadscaleEnv(spec)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
headscale, err := scenario.Headscale()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var preAuthReusableKey v1.PreAuthKey
|
var preAuthReusableKey v1.PreAuthKey
|
||||||
err = executeAndUnmarshal(
|
err = executeAndUnmarshal(
|
||||||
scenario.MustHeadscale(),
|
headscale,
|
||||||
[]string{
|
[]string{
|
||||||
"headscale",
|
"headscale",
|
||||||
"preauthkeys",
|
"preauthkeys",
|
||||||
@ -335,7 +347,7 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) {
|
|||||||
|
|
||||||
var preAuthEphemeralKey v1.PreAuthKey
|
var preAuthEphemeralKey v1.PreAuthKey
|
||||||
err = executeAndUnmarshal(
|
err = executeAndUnmarshal(
|
||||||
scenario.MustHeadscale(),
|
headscale,
|
||||||
[]string{
|
[]string{
|
||||||
"headscale",
|
"headscale",
|
||||||
"preauthkeys",
|
"preauthkeys",
|
||||||
@ -355,7 +367,7 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) {
|
|||||||
|
|
||||||
var listedPreAuthKeys []v1.PreAuthKey
|
var listedPreAuthKeys []v1.PreAuthKey
|
||||||
err = executeAndUnmarshal(
|
err = executeAndUnmarshal(
|
||||||
scenario.MustHeadscale(),
|
headscale,
|
||||||
[]string{
|
[]string{
|
||||||
"headscale",
|
"headscale",
|
||||||
"preauthkeys",
|
"preauthkeys",
|
||||||
|
@ -13,4 +13,7 @@ type ControlServer interface {
|
|||||||
CreateNamespace(namespace string) error
|
CreateNamespace(namespace string) error
|
||||||
CreateAuthKey(namespace string) (*v1.PreAuthKey, error)
|
CreateAuthKey(namespace string) (*v1.PreAuthKey, error)
|
||||||
ListMachinesInNamespace(namespace string) ([]*v1.Machine, error)
|
ListMachinesInNamespace(namespace string) ([]*v1.Machine, error)
|
||||||
|
GetCert() []byte
|
||||||
|
GetHostname() string
|
||||||
|
GetIP() string
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/juanfont/headscale/integration/hsic"
|
"github.com/juanfont/headscale/integration/hsic"
|
||||||
"github.com/juanfont/headscale/integration/tsic"
|
"github.com/juanfont/headscale/integration/tsic"
|
||||||
"github.com/ory/dockertest/v3"
|
"github.com/ory/dockertest/v3"
|
||||||
|
"github.com/puzpuzpuz/xsync/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -69,12 +70,14 @@ type Namespace struct {
|
|||||||
type Scenario struct {
|
type Scenario struct {
|
||||||
// TODO(kradalby): support multiple headcales for later, currently only
|
// TODO(kradalby): support multiple headcales for later, currently only
|
||||||
// use one.
|
// use one.
|
||||||
controlServers map[string]ControlServer
|
controlServers *xsync.MapOf[string, ControlServer]
|
||||||
|
|
||||||
namespaces map[string]*Namespace
|
namespaces map[string]*Namespace
|
||||||
|
|
||||||
pool *dockertest.Pool
|
pool *dockertest.Pool
|
||||||
network *dockertest.Network
|
network *dockertest.Network
|
||||||
|
|
||||||
|
headscaleLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewScenario() (*Scenario, error) {
|
func NewScenario() (*Scenario, error) {
|
||||||
@ -109,7 +112,7 @@ func NewScenario() (*Scenario, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &Scenario{
|
return &Scenario{
|
||||||
controlServers: make(map[string]ControlServer),
|
controlServers: xsync.NewMapOf[ControlServer](),
|
||||||
namespaces: make(map[string]*Namespace),
|
namespaces: make(map[string]*Namespace),
|
||||||
|
|
||||||
pool: pool,
|
pool: pool,
|
||||||
@ -118,13 +121,18 @@ func NewScenario() (*Scenario, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scenario) Shutdown() error {
|
func (s *Scenario) Shutdown() error {
|
||||||
for _, control := range s.controlServers {
|
s.controlServers.Range(func(_ string, control ControlServer) bool {
|
||||||
err := control.Shutdown()
|
err := control.Shutdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to tear down control: %w", err)
|
log.Printf(
|
||||||
}
|
"Failed to shut down control: %s",
|
||||||
|
fmt.Errorf("failed to tear down control: %w", err),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
for namespaceName, namespace := range s.namespaces {
|
for namespaceName, namespace := range s.namespaces {
|
||||||
for _, client := range namespace.Clients {
|
for _, client := range namespace.Clients {
|
||||||
log.Printf("removing client %s in namespace %s", client.Hostname(), namespaceName)
|
log.Printf("removing client %s in namespace %s", client.Hostname(), namespaceName)
|
||||||
@ -160,31 +168,31 @@ func (s *Scenario) Namespaces() []string {
|
|||||||
// Note: These functions assume that there is a _single_ headscale instance for now
|
// Note: These functions assume that there is a _single_ headscale instance for now
|
||||||
|
|
||||||
// TODO(kradalby): make port and headscale configurable, multiple instances support?
|
// TODO(kradalby): make port and headscale configurable, multiple instances support?
|
||||||
func (s *Scenario) StartHeadscale(opts ...hsic.Option) error {
|
func (s *Scenario) Headscale(opts ...hsic.Option) (ControlServer, error) {
|
||||||
|
s.headscaleLock.Lock()
|
||||||
|
defer s.headscaleLock.Unlock()
|
||||||
|
|
||||||
|
if headscale, ok := s.controlServers.Load("headscale"); ok {
|
||||||
|
return headscale, nil
|
||||||
|
}
|
||||||
|
|
||||||
headscale, err := hsic.New(s.pool, s.network, opts...)
|
headscale, err := hsic.New(s.pool, s.network, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create headscale container: %w", err)
|
return nil, fmt.Errorf("failed to create headscale container: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = headscale.WaitForReady()
|
err = headscale.WaitForReady()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, fmt.Errorf("failed reach headscale container: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.controlServers["headscale"] = headscale
|
s.controlServers.Store("headscale", headscale)
|
||||||
|
|
||||||
return nil
|
return headscale, nil
|
||||||
}
|
|
||||||
|
|
||||||
// MustHeadscale returns the headscale unit of a scenario, it will crash if it
|
|
||||||
// is not available.
|
|
||||||
func (s *Scenario) MustHeadscale() *hsic.HeadscaleInContainer {
|
|
||||||
//nolint
|
|
||||||
return s.controlServers["headscale"].(*hsic.HeadscaleInContainer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scenario) CreatePreAuthKey(namespace string) (*v1.PreAuthKey, error) {
|
func (s *Scenario) CreatePreAuthKey(namespace string) (*v1.PreAuthKey, error) {
|
||||||
if headscale, ok := s.controlServers["headscale"]; ok {
|
if headscale, err := s.Headscale(); err == nil {
|
||||||
key, err := headscale.CreateAuthKey(namespace)
|
key, err := headscale.CreateAuthKey(namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create namespace: %w", err)
|
return nil, fmt.Errorf("failed to create namespace: %w", err)
|
||||||
@ -197,7 +205,7 @@ func (s *Scenario) CreatePreAuthKey(namespace string) (*v1.PreAuthKey, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scenario) CreateNamespace(namespace string) error {
|
func (s *Scenario) CreateNamespace(namespace string) error {
|
||||||
if headscale, ok := s.controlServers["headscale"]; ok {
|
if headscale, err := s.Headscale(); err == nil {
|
||||||
err := headscale.CreateNamespace(namespace)
|
err := headscale.CreateNamespace(namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create namespace: %w", err)
|
return fmt.Errorf("failed to create namespace: %w", err)
|
||||||
@ -227,6 +235,14 @@ func (s *Scenario) CreateTailscaleNodesInNamespace(
|
|||||||
version = TailscaleVersions[i%len(TailscaleVersions)]
|
version = TailscaleVersions[i%len(TailscaleVersions)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
headscale, err := s.Headscale()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create tailscale node: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert := headscale.GetCert()
|
||||||
|
hostname := headscale.GetHostname()
|
||||||
|
|
||||||
namespace.createWaitGroup.Add(1)
|
namespace.createWaitGroup.Add(1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -237,8 +253,8 @@ func (s *Scenario) CreateTailscaleNodesInNamespace(
|
|||||||
s.pool,
|
s.pool,
|
||||||
version,
|
version,
|
||||||
s.network,
|
s.network,
|
||||||
tsic.WithHeadscaleTLS(s.MustHeadscale().GetCert()),
|
tsic.WithHeadscaleTLS(cert),
|
||||||
tsic.WithHeadscaleName(s.MustHeadscale().GetHostname()),
|
tsic.WithHeadscaleName(hostname),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// return fmt.Errorf("failed to add tailscale node: %w", err)
|
// return fmt.Errorf("failed to add tailscale node: %w", err)
|
||||||
@ -324,7 +340,7 @@ func (s *Scenario) WaitForTailscaleSync() error {
|
|||||||
// test environment with nodes of all versions, joined to the server with X
|
// test environment with nodes of all versions, joined to the server with X
|
||||||
// namespaces.
|
// namespaces.
|
||||||
func (s *Scenario) CreateHeadscaleEnv(namespaces map[string]int, opts ...hsic.Option) error {
|
func (s *Scenario) CreateHeadscaleEnv(namespaces map[string]int, opts ...hsic.Option) error {
|
||||||
err := s.StartHeadscale(opts...)
|
headscale, err := s.Headscale(opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -345,7 +361,7 @@ func (s *Scenario) CreateHeadscaleEnv(namespaces map[string]int, opts ...hsic.Op
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.RunTailscaleUp(namespaceName, s.MustHeadscale().GetEndpoint(), key.GetKey())
|
err = s.RunTailscaleUp(namespaceName, headscale.GetEndpoint(), key.GetKey())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -34,12 +34,12 @@ func TestHeadscale(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run("start-headscale", func(t *testing.T) {
|
t.Run("start-headscale", func(t *testing.T) {
|
||||||
err = scenario.StartHeadscale()
|
headscale, err := scenario.Headscale()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to create start headcale: %s", err)
|
t.Errorf("failed to create start headcale: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = scenario.MustHeadscale().WaitForReady()
|
err = headscale.WaitForReady()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("headscale failed to become ready: %s", err)
|
t.Errorf("headscale failed to become ready: %s", err)
|
||||||
}
|
}
|
||||||
@ -117,12 +117,11 @@ func TestTailscaleNodesJoiningHeadcale(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run("start-headscale", func(t *testing.T) {
|
t.Run("start-headscale", func(t *testing.T) {
|
||||||
err = scenario.StartHeadscale()
|
headscale, err := scenario.Headscale()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to create start headcale: %s", err)
|
t.Errorf("failed to create start headcale: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
headscale := scenario.MustHeadscale()
|
|
||||||
err = headscale.WaitForReady()
|
err = headscale.WaitForReady()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("headscale failed to become ready: %s", err)
|
t.Errorf("headscale failed to become ready: %s", err)
|
||||||
@ -157,7 +156,16 @@ func TestTailscaleNodesJoiningHeadcale(t *testing.T) {
|
|||||||
t.Errorf("failed to create preauthkey: %s", err)
|
t.Errorf("failed to create preauthkey: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = scenario.RunTailscaleUp(namespace, scenario.MustHeadscale().GetEndpoint(), key.GetKey())
|
headscale, err := scenario.Headscale()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to create start headcale: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = scenario.RunTailscaleUp(
|
||||||
|
namespace,
|
||||||
|
headscale.GetEndpoint(),
|
||||||
|
key.GetKey(),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to login: %s", err)
|
t.Errorf("failed to login: %s", err)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user