diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration.yml
index 2ac023ac..70b36b1c 100644
--- a/.github/workflows/test-integration.yml
+++ b/.github/workflows/test-integration.yml
@@ -27,4 +27,9 @@ jobs:
- name: Run Integration tests
if: steps.changed-files.outputs.any_changed == 'true'
- run: nix develop --command -- make test_integration
+ uses: nick-fields/retry@v2
+ with:
+ timeout_minutes: 240
+ max_attempts: 5
+ retry_on: error
+ command: nix develop --command -- make test_integration
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9e916056..d9a3a132 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
# CHANGELOG
-## 0.16.0 (2022-xx-xx)
+## 0.17.0 (2022-xx-xx)
+
+## 0.16.0 (2022-07-25)
+
+**Note:** Take a backup of your database before upgrading.
### BREAKING
@@ -30,11 +34,12 @@
- Add -c option to specify config file from command line [#285](https://github.com/juanfont/headscale/issues/285) [#612](https://github.com/juanfont/headscale/pull/601)
- Add configuration option to allow Tailscale clients to use a random WireGuard port. [kb/1181/firewalls](https://tailscale.com/kb/1181/firewalls) [#624](https://github.com/juanfont/headscale/pull/624)
- Improve obtuse UX regarding missing configuration (`ephemeral_node_inactivity_timeout` not set) [#639](https://github.com/juanfont/headscale/pull/639)
-- Fix nodes being shown as 'offline' in `tailscale status` [648](https://github.com/juanfont/headscale/pull/648)
- Fix nodes being shown as 'offline' in `tailscale status` [#648](https://github.com/juanfont/headscale/pull/648)
- Improve shutdown behaviour [#651](https://github.com/juanfont/headscale/pull/651)
-- Drop Gin as web framework in Headscale [648](https://github.com/juanfont/headscale/pull/648)
-
+- Drop Gin as web framework in Headscale [648](https://github.com/juanfont/headscale/pull/648) [677](https://github.com/juanfont/headscale/pull/677)
+- Make tailnet node updates check interval configurable [#675](https://github.com/juanfont/headscale/pull/675)
+- Fix regression with HTTP API [#684](https://github.com/juanfont/headscale/pull/684)
+- nodes ls now print both Hostname and Name(Issue [#647](https://github.com/juanfont/headscale/issues/647) PR [#687](https://github.com/juanfont/headscale/pull/687))
## 0.15.0 (2022-03-20)
diff --git a/Dockerfile b/Dockerfile
index b9139f09..33aa5780 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
# Builder image
-FROM docker.io/golang:1.18.0-bullseye AS build
+FROM --platform=$BUILDPLATFORM docker.io/golang:1.18.0-bullseye AS build
ARG VERSION=dev
ENV GOPATH /go
WORKDIR /go/src/headscale
@@ -8,9 +8,8 @@ COPY go.mod go.sum /go/src/headscale/
RUN go mod download
COPY . .
-
-RUN CGO_ENABLED=0 GOOS=linux go install -ldflags="-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$VERSION" -a ./cmd/headscale
-RUN strip /go/bin/headscale
+ARG TARGETOS TARGETARCH
+RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /go/bin/headscale -ldflags="-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$VERSION" -a ./cmd/headscale
RUN test -e /go/bin/headscale
# Production image
diff --git a/Dockerfile.alpine b/Dockerfile.alpine
index de683f2d..b8d9e33a 100644
--- a/Dockerfile.alpine
+++ b/Dockerfile.alpine
@@ -1,5 +1,5 @@
# Builder image
-FROM docker.io/golang:1.18.0-alpine AS build
+FROM --platform=$BUILDPLATFORM docker.io/golang:1.18.0-alpine AS build
ARG VERSION=dev
ENV GOPATH /go
WORKDIR /go/src/headscale
@@ -10,8 +10,8 @@ RUN go mod download
COPY . .
-RUN CGO_ENABLED=0 GOOS=linux go install -ldflags="-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$VERSION" -a ./cmd/headscale
-RUN strip /go/bin/headscale
+ARG TARGETOS TARGETARCH
+RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /go/bin/headscale -ldflags="-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$VERSION" -a ./cmd/headscale
RUN test -e /go/bin/headscale
# Production image
diff --git a/Dockerfile.debug b/Dockerfile.debug
index 9b1b7c6b..4333e94d 100644
--- a/Dockerfile.debug
+++ b/Dockerfile.debug
@@ -1,5 +1,5 @@
# Builder image
-FROM docker.io/golang:1.18.0-bullseye AS build
+FROM --platform=$BUILDPLATFORM docker.io/golang:1.18.0-bullseye AS build
ARG VERSION=dev
ENV GOPATH /go
WORKDIR /go/src/headscale
@@ -9,6 +9,7 @@ RUN go mod download
COPY . .
+ARG TARGETOS TARGETARCH
RUN CGO_ENABLED=0 GOOS=linux go install -ldflags="-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$VERSION" -a ./cmd/headscale
RUN test -e /go/bin/headscale
diff --git a/README.md b/README.md
index 262d5535..d0887675 100644
--- a/README.md
+++ b/README.md
@@ -188,13 +188,6 @@ make build
Ward Vandewege
-
-
-
-
- Nico
-
- |
@@ -202,6 +195,13 @@ make build
Jiang Zhu
|
+
+
+
+
+ Nico
+
+ |
@@ -283,6 +283,15 @@ make build
Fernando De Lucchi
|
+
+
+
+
+ GrigoriyMikhalkin
+
+ |
+
+
@@ -290,8 +299,6 @@ make build
Hoàng Đức Hiếu
|
-
-
@@ -327,6 +334,8 @@ make build
Michael G.
|
+
+
@@ -334,8 +343,6 @@ make build
Paul Tötterman
|
-
-
@@ -371,6 +378,8 @@ make build
Pavlos Vinieratos
|
+
+
@@ -378,8 +387,6 @@ make build
Silver Bullet
|
-
-
@@ -415,6 +422,8 @@ make build
Aofei Sheng
|
+
+
@@ -422,8 +431,6 @@ make build
Arthur Woimbée
|
-
-
@@ -459,6 +466,8 @@ make build
Felix Yan
|
+
+
@@ -466,8 +475,6 @@ make build
JJGadgets
|
-
-
@@ -503,6 +510,8 @@ make build
WhiteSource Renovate
|
+
+
@@ -510,8 +519,6 @@ make build
Ryan Fowler
|
-
-
@@ -547,6 +554,8 @@ make build
Tianon Gravi
|
+
+
@@ -554,8 +563,6 @@ make build
Tjerk Woudsma
|
-
-
@@ -572,9 +579,9 @@ make build
|
-
+
- ZiYuan
+ Ziyuan Han
|
@@ -591,6 +598,8 @@ make build
henning mueller
|
+
+
@@ -598,8 +607,6 @@ make build
ignoramous
|
-
-
diff --git a/api.go b/api.go
index fc27e46b..ff0de0c4 100644
--- a/api.go
+++ b/api.go
@@ -30,6 +30,44 @@ const (
)
)
+func (h *Headscale) HealthHandler(
+ writer http.ResponseWriter,
+ req *http.Request,
+) {
+ respond := func(err error) {
+ writer.Header().Set("Content-Type", "application/health+json; charset=utf-8")
+
+ res := struct {
+ Status string `json:"status"`
+ }{
+ Status: "pass",
+ }
+
+ if err != nil {
+ writer.WriteHeader(http.StatusInternalServerError)
+ log.Error().Caller().Err(err).Msg("health check failed")
+ res.Status = "fail"
+ }
+
+ buf, err := json.Marshal(res)
+ if err != nil {
+ log.Error().Caller().Err(err).Msg("marshal failed")
+ }
+ _, err = writer.Write(buf)
+ if err != nil {
+ log.Error().Caller().Err(err).Msg("write failed")
+ }
+ }
+
+ if err := h.pingDB(); err != nil {
+ respond(err)
+
+ return
+ }
+
+ respond(nil)
+}
+
// KeyHandler provides the Headscale pub key
// Listens in /key.
func (h *Headscale) KeyHandler(
diff --git a/app.go b/app.go
index e4e69105..bd88dedf 100644
--- a/app.go
+++ b/app.go
@@ -17,17 +17,16 @@ import (
"time"
"github.com/coreos/go-oidc/v3/oidc"
- "github.com/gin-gonic/gin"
"github.com/gorilla/mux"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/patrickmn/go-cache"
zerolog "github.com/philip-bui/grpc-zerolog"
+ "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/puzpuzpuz/xsync"
zl "github.com/rs/zerolog"
"github.com/rs/zerolog/log"
- ginprometheus "github.com/zsais/go-gin-prometheus"
"golang.org/x/crypto/acme"
"golang.org/x/crypto/acme/autocert"
"golang.org/x/oauth2"
@@ -95,7 +94,8 @@ type Headscale struct {
ipAllocationMutex sync.Mutex
- shutdownChan chan struct{}
+ shutdownChan chan struct{}
+ pollNetMapStreamWG sync.WaitGroup
}
// Look up the TLS constant relative to user-supplied TLS client
@@ -148,12 +148,13 @@ func NewHeadscale(cfg *Config) (*Headscale, error) {
)
app := Headscale{
- cfg: cfg,
- dbType: cfg.DBtype,
- dbString: dbString,
- privateKey: privKey,
- aclRules: tailcfg.FilterAllowAll, // default allowall
- registrationCache: registrationCache,
+ cfg: cfg,
+ dbType: cfg.DBtype,
+ dbString: dbString,
+ privateKey: privKey,
+ aclRules: tailcfg.FilterAllowAll, // default allowall
+ registrationCache: registrationCache,
+ pollNetMapStreamWG: sync.WaitGroup{},
}
err = app.initDB()
@@ -411,31 +412,10 @@ func (h *Headscale) ensureUnixSocketIsAbsent() error {
return os.Remove(h.cfg.UnixSocket)
}
-func (h *Headscale) createPrometheusRouter() *gin.Engine {
- promRouter := gin.Default()
-
- prometheus := ginprometheus.NewPrometheus("gin")
- prometheus.Use(promRouter)
-
- return promRouter
-}
-
func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *mux.Router {
router := mux.NewRouter()
- router.HandleFunc(
- "/health",
- func(writer http.ResponseWriter, req *http.Request) {
- writer.WriteHeader(http.StatusOK)
- _, err := writer.Write([]byte("{\"healthy\": \"ok\"}"))
- if err != nil {
- log.Error().
- Caller().
- Err(err).
- Msg("Failed to write response")
- }
- }).Methods(http.MethodGet)
-
+ router.HandleFunc("/health", h.HealthHandler).Methods(http.MethodGet)
router.HandleFunc("/key", h.KeyHandler).Methods(http.MethodGet)
router.HandleFunc("/register", h.RegisterWebAPI).Methods(http.MethodGet)
router.HandleFunc("/machine/{mkey}/map", h.PollNetMapHandler).Methods(http.MethodPost)
@@ -455,11 +435,9 @@ func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *mux.Router {
router.HandleFunc("/bootstrap-dns", h.DERPBootstrapDNSHandler)
}
- api := router.PathPrefix("/api").Subrouter()
- api.Use(h.httpAuthenticationMiddleware)
- {
- api.HandleFunc("/v1/*any", grpcMux.ServeHTTP)
- }
+ apiRouter := router.PathPrefix("/api").Subrouter()
+ apiRouter.Use(h.httpAuthenticationMiddleware)
+ apiRouter.PathPrefix("/v1/").HandlerFunc(grpcMux.ServeHTTP)
router.PathPrefix("/").HandlerFunc(stdoutHandler)
@@ -577,6 +555,8 @@ func (h *Headscale) Serve() error {
// https://github.com/soheilhy/cmux/issues/68
// https://github.com/soheilhy/cmux/issues/91
+ var grpcServer *grpc.Server
+ var grpcListener net.Listener
if tlsConfig != nil || h.cfg.GRPCAllowInsecure {
log.Info().Msgf("Enabling remote gRPC at %s", h.cfg.GRPCAddr)
@@ -597,12 +577,12 @@ func (h *Headscale) Serve() error {
log.Warn().Msg("gRPC is running without security")
}
- grpcServer := grpc.NewServer(grpcOptions...)
+ grpcServer = grpc.NewServer(grpcOptions...)
v1.RegisterHeadscaleServiceServer(grpcServer, newHeadscaleV1APIServer(h))
reflection.Register(grpcServer)
- grpcListener, err := net.Listen("tcp", h.cfg.GRPCAddr)
+ grpcListener, err = net.Listen("tcp", h.cfg.GRPCAddr)
if err != nil {
return fmt.Errorf("failed to bind to TCP address: %w", err)
}
@@ -647,11 +627,12 @@ func (h *Headscale) Serve() error {
log.Info().
Msgf("listening and serving HTTP on: %s", h.cfg.Addr)
- promRouter := h.createPrometheusRouter()
+ promMux := http.NewServeMux()
+ promMux.Handle("/metrics", promhttp.Handler())
promHTTPServer := &http.Server{
Addr: h.cfg.MetricsAddr,
- Handler: promRouter,
+ Handler: promMux,
ReadTimeout: HTTPReadTimeout,
WriteTimeout: 0,
}
@@ -677,7 +658,7 @@ func (h *Headscale) Serve() error {
syscall.SIGTERM,
syscall.SIGQUIT,
syscall.SIGHUP)
- go func(c chan os.Signal) {
+ sigFunc := func(c chan os.Signal) {
// Wait for a SIGINT or SIGKILL:
for {
sig := <-c
@@ -687,7 +668,7 @@ func (h *Headscale) Serve() error {
Str("signal", sig.String()).
Msg("Received SIGHUP, reloading ACL and Config")
- // TODO(kradalby): Reload config on SIGHUP
+ // TODO(kradalby): Reload config on SIGHUP
if h.cfg.ACL.PolicyPath != "" {
aclPath := AbsolutePathFromConfigPath(h.cfg.ACL.PolicyPath)
@@ -707,7 +688,8 @@ func (h *Headscale) Serve() error {
Str("signal", sig.String()).
Msg("Received signal to stop, shutting down gracefully")
- h.shutdownChan <- struct{}{}
+ close(h.shutdownChan)
+ h.pollNetMapStreamWG.Wait()
// Gracefully shut down servers
ctx, cancel := context.WithTimeout(context.Background(), HTTPShutdownTimeout)
@@ -719,6 +701,11 @@ func (h *Headscale) Serve() error {
}
grpcSocket.GracefulStop()
+ if grpcServer != nil {
+ grpcServer.GracefulStop()
+ grpcListener.Close()
+ }
+
// Close network listeners
promHTTPListener.Close()
httpListener.Close()
@@ -745,7 +732,12 @@ func (h *Headscale) Serve() error {
os.Exit(0)
}
}
- }(sigc)
+ }
+ errorGroup.Go(func() error {
+ sigFunc(sigc)
+
+ return nil
+ })
return errorGroup.Wait()
}
@@ -769,13 +761,13 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) {
}
switch h.cfg.TLS.LetsEncrypt.ChallengeType {
- case "TLS-ALPN-01":
+ case tlsALPN01ChallengeType:
// Configuration via autocert with TLS-ALPN-01 (https://tools.ietf.org/html/rfc8737)
// The RFC requires that the validation is done on port 443; in other words, headscale
// must be reachable on port 443.
return certManager.TLSConfig(), nil
- case "HTTP-01":
+ case http01ChallengeType:
// Configuration via autocert with HTTP-01. This requires listening on
// port 80 for the certificate validation in addition to the headscale
// service, which can be configured to run on any other port.
diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go
index 059a16df..c2b1e950 100644
--- a/cmd/headscale/cli/nodes.go
+++ b/cmd/headscale/cli/nodes.go
@@ -465,6 +465,7 @@ func nodesToPtables(
) (pterm.TableData, error) {
tableHeader := []string{
"ID",
+ "Hostname",
"Name",
"NodeKey",
"Namespace",
@@ -566,6 +567,7 @@ func nodesToPtables(
nodeData := []string{
strconv.FormatUint(machine.Id, headscale.Base10),
machine.Name,
+ machine.GetGivenName(),
nodeKey.ShortString(),
namespace,
strings.Join([]string{IPV4Address, IPV6Address}, ", "),
diff --git a/config-example.yaml b/config-example.yaml
index 9740f3ad..d3d155e2 100644
--- a/config-example.yaml
+++ b/config-example.yaml
@@ -103,6 +103,12 @@ disable_check_updates: false
# Time before an inactive ephemeral node is deleted?
ephemeral_node_inactivity_timeout: 30m
+# Period to check for node updates in the tailnet. A value too low will severily affect
+# CPU consumption of Headscale. A value too high (over 60s) will cause problems
+# to the nodes, as they won't get updates or keep alive messages in time.
+# In case of doubts, do not touch the default 10s.
+node_update_check_interval: 10s
+
# SQLite config
db_type: sqlite3
db_path: /var/lib/headscale/db.sqlite
diff --git a/config.go b/config.go
index 9e71a750..69358401 100644
--- a/config.go
+++ b/config.go
@@ -18,6 +18,11 @@ import (
"tailscale.com/types/dnstype"
)
+const (
+ tlsALPN01ChallengeType = "TLS-ALPN-01"
+ http01ChallengeType = "HTTP-01"
+)
+
// Config contains the initial Headscale configuration.
type Config struct {
ServerURL string
@@ -26,6 +31,7 @@ type Config struct {
GRPCAddr string
GRPCAllowInsecure bool
EphemeralNodeInactivityTimeout time.Duration
+ NodeUpdateCheckInterval time.Duration
IPPrefixes []netaddr.IPPrefix
PrivateKeyPath string
BaseDomain string
@@ -135,7 +141,7 @@ func LoadConfig(path string, isFile bool) error {
viper.AutomaticEnv()
viper.SetDefault("tls_letsencrypt_cache_dir", "/var/www/.cache")
- viper.SetDefault("tls_letsencrypt_challenge_type", "HTTP-01")
+ viper.SetDefault("tls_letsencrypt_challenge_type", http01ChallengeType)
viper.SetDefault("tls_client_auth_mode", "relaxed")
viper.SetDefault("log_level", "info")
@@ -162,6 +168,8 @@ func LoadConfig(path string, isFile bool) error {
viper.SetDefault("ephemeral_node_inactivity_timeout", "120s")
+ viper.SetDefault("node_update_check_interval", "10s")
+
if err := viper.ReadInConfig(); err != nil {
log.Warn().Err(err).Msg("Failed to read configuration from disk")
@@ -176,15 +184,15 @@ func LoadConfig(path string, isFile bool) error {
}
if (viper.GetString("tls_letsencrypt_hostname") != "") &&
- (viper.GetString("tls_letsencrypt_challenge_type") == "TLS-ALPN-01") &&
+ (viper.GetString("tls_letsencrypt_challenge_type") == tlsALPN01ChallengeType) &&
(!strings.HasSuffix(viper.GetString("listen_addr"), ":443")) {
// this is only a warning because there could be something sitting in front of headscale that redirects the traffic (e.g. an iptables rule)
log.Warn().
Msg("Warning: when using tls_letsencrypt_hostname with TLS-ALPN-01 as challenge type, headscale must be reachable on port 443, i.e. listen_addr should probably end in :443")
}
- if (viper.GetString("tls_letsencrypt_challenge_type") != "HTTP-01") &&
- (viper.GetString("tls_letsencrypt_challenge_type") != "TLS-ALPN-01") {
+ if (viper.GetString("tls_letsencrypt_challenge_type") != http01ChallengeType) &&
+ (viper.GetString("tls_letsencrypt_challenge_type") != tlsALPN01ChallengeType) {
errorText += "Fatal config error: the only supported values for tls_letsencrypt_challenge_type are HTTP-01 and TLS-ALPN-01\n"
}
@@ -217,6 +225,15 @@ func LoadConfig(path string, isFile bool) error {
)
}
+ maxNodeUpdateCheckInterval, _ := time.ParseDuration("60s")
+ if viper.GetDuration("node_update_check_interval") > maxNodeUpdateCheckInterval {
+ errorText += fmt.Sprintf(
+ "Fatal config error: node_update_check_interval (%s) is set too high, must be less than %s",
+ viper.GetString("node_update_check_interval"),
+ maxNodeUpdateCheckInterval,
+ )
+ }
+
if errorText != "" {
//nolint
return errors.New(strings.TrimSuffix(errorText, "\n"))
@@ -478,6 +495,10 @@ func GetHeadscaleConfig() (*Config, error) {
"ephemeral_node_inactivity_timeout",
),
+ NodeUpdateCheckInterval: viper.GetDuration(
+ "node_update_check_interval",
+ ),
+
DBtype: viper.GetString("db_type"),
DBpath: AbsolutePathFromConfigPath(viper.GetString("db_path")),
DBhost: viper.GetString("db_host"),
diff --git a/db.go b/db.go
index e412468d..5df9c23b 100644
--- a/db.go
+++ b/db.go
@@ -1,6 +1,7 @@
package headscale
import (
+ "context"
"database/sql/driver"
"encoding/json"
"errors"
@@ -220,6 +221,17 @@ func (h *Headscale) setValue(key string, value string) error {
return nil
}
+func (h *Headscale) pingDB() error {
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+ defer cancel()
+ db, err := h.db.DB()
+ if err != nil {
+ return err
+ }
+
+ return db.PingContext(ctx)
+}
+
// This is a "wrapper" type around tailscales
// Hostinfo to allow us to add database "serialization"
// methods. This allows us to use a typed values throughout
diff --git a/docs/build-headscale-container.md b/docs/build-headscale-container.md
new file mode 100644
index 00000000..b0220167
--- /dev/null
+++ b/docs/build-headscale-container.md
@@ -0,0 +1,32 @@
+# Build docker from scratch
+
+The Dockerfiles included in the repository are using the [buildx plugin](https://docs.docker.com/buildx/working-with-buildx/). This plugin is includes in docker newer than Docker-ce CLI 19.03.2. The plugin is used to be able to build different container arches. Building the Dockerfiles without buildx is not possible.
+
+# Build native
+
+To build the container on the native arch you can just use:
+```
+$ sudo docker buildx build -t headscale:custom-arch .
+```
+
+For example: This will build a amd64(x86_64) container if your hostsystem is amd64(x86_64). Or a arm64 container on a arm64 hostsystem (raspberry pi4).
+
+# Build cross platform
+
+To build a arm64 container on a amd64 hostsystem you could use:
+```
+$ sudo docker buildx build --platform linux/arm64 -t headscale:custom-arm64 .
+
+```
+
+**Import: Currently arm32 build are not supported as there is a problem with a library used by headscale. Hopefully this will be fixed soon.**
+
+# Build multiple arches
+
+To build multiple archres you could use:
+
+```
+$ sudo docker buildx create --use
+$ sudo docker buildx build --platform linux/amd64,linux/arm64 .
+
+```
diff --git a/flake.nix b/flake.nix
index afa8c8bb..ec83d41c 100644
--- a/flake.nix
+++ b/flake.nix
@@ -24,7 +24,7 @@
# 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.
- vendorSha256 = "sha256-T6rH+aqofFmCPxDfoA5xd3kNUJeZkT4GRyuFEnenps8=";
+ vendorSha256 = "sha256-2o78hsi0B9U5NOcYXRqkBmg34p71J/R8FibXsgwEcSo=";
ldflags = [ "-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}" ];
};
diff --git a/go.mod b/go.mod
index e10ae35e..4784ef29 100644
--- a/go.mod
+++ b/go.mod
@@ -8,14 +8,13 @@ require (
github.com/coreos/go-oidc/v3 v3.1.0
github.com/deckarep/golang-set/v2 v2.1.0
github.com/efekarakus/termcolor v1.0.1
- github.com/gin-gonic/gin v1.7.7
github.com/glebarez/sqlite v1.4.3
github.com/gofrs/uuid v4.2.0+incompatible
github.com/gorilla/mux v1.8.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.0
github.com/klauspost/compress v1.15.4
- github.com/ory/dockertest/v3 v3.8.1
+ github.com/ory/dockertest/v3 v3.9.1
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/philip-bui/grpc-zerolog v1.0.1
github.com/prometheus/client_golang v1.12.1
@@ -28,7 +27,6 @@ require (
github.com/stretchr/testify v1.7.1
github.com/tailscale/hujson v0.0.0-20220506202205-92b4b88a9e17
github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e
- github.com/zsais/go-gin-prometheus v0.1.0
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
@@ -52,20 +50,16 @@ require (
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
github.com/atomicgo/cursor v0.0.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
- github.com/cenkalti/backoff/v4 v4.1.2 // indirect
+ github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
- github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 // indirect
+ github.com/containerd/continuity v0.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/cli v20.10.16+incompatible // indirect
github.com/docker/docker v20.10.16+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
- github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.16.0 // indirect
- github.com/go-playground/locales v0.13.0 // indirect
- github.com/go-playground/universal-translator v0.17.0 // indirect
- github.com/go-playground/validator/v10 v10.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.8 // indirect
@@ -90,11 +84,9 @@ require (
github.com/jinzhu/now v1.1.4 // indirect
github.com/josharian/native v1.0.0 // indirect
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b // indirect
- github.com/json-iterator/go v1.1.12 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/kr/text v0.2.0 // indirect
- github.com/leodido/go-urn v1.2.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
@@ -106,11 +98,9 @@ require (
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
- github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
- github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect
- github.com/opencontainers/runc v1.0.2 // indirect
+ github.com/opencontainers/runc v1.1.2 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect
github.com/pkg/errors v0.9.1 // indirect
@@ -126,7 +116,6 @@ require (
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
- github.com/ugorji/go/codec v1.1.7 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
diff --git a/go.sum b/go.sum
index b4d03b90..c2a781ae 100644
--- a/go.sum
+++ b/go.sum
@@ -131,6 +131,8 @@ github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029 h1:POmUHfxXdey
github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029/go.mod h1:Rpr5n9cGHYdM3S3IK8ROSUUUYjQOu+MSUCZDcJbYWi8=
github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
+github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
+github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -139,10 +141,12 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg=
github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3//PWVzTeCezG2b9IRn6myJxJSr4TD/xo6ojU=
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
+github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
+github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
github.com/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao=
github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@@ -159,8 +163,11 @@ github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
+github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 h1:NmTXa/uVnDyp0TY5MKi197+3HWcnYWfnHGyaFthlnGw=
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
+github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw=
@@ -184,6 +191,7 @@ github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
+github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/daixiang0/gci v0.2.9/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -236,10 +244,6 @@ github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5
github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM=
github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
-github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
-github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
-github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/glebarez/go-sqlite v1.16.0 h1:h28rHued+hGof3fNLksBcLwz/a71fiGZ/eIJHK0SsLI=
github.com/glebarez/go-sqlite v1.16.0/go.mod h1:i8/JtqoqzBAFkrUTxbQFkQ05odCOds3j7NlDaXjqiPY=
github.com/glebarez/sqlite v1.4.3 h1:ZABNo+2YIau8F8sZ7Qh/1h/ZnlSUMHFGD4zJKPval7A=
@@ -256,14 +260,6 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
-github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
-github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
-github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
-github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
-github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
-github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
-github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
-github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
@@ -283,6 +279,7 @@ github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
@@ -546,10 +543,8 @@ github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b h1:Yws7RV6k
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b/go.mod h1:TzDCVOZKUa79z6iXbbXqhtAflVgUKaFkZ21M5tK5tzY=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
@@ -595,8 +590,6 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg=
github.com/ldez/gomoddirectives v0.2.2/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0=
github.com/ldez/tagliatelle v0.2.0/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88=
-github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
-github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag=
github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@@ -683,14 +676,13 @@ github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
+github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k=
@@ -728,11 +720,16 @@ github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 h1:+cz
github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198/go.mod h1:j4h1pJW6ZcJTgMZWP3+7RlG3zTaP02aDZ/Qw0sppK7Q=
github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg=
github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
+github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw=
+github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
+github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/ory/dockertest/v3 v3.8.1 h1:vU/8d1We4qIad2YM0kOwRVtnyue7ExvacPiw1yDm17g=
github.com/ory/dockertest/v3 v3.8.1/go.mod h1:wSRQ3wmkz+uSARYMk7kVJFDBGm8x5gSxIhI7NDc+BAQ=
+github.com/ory/dockertest/v3 v3.9.1 h1:v4dkG+dlu76goxMiTT2j8zV7s4oPPEppKT8K8p2f1kY=
+github.com/ory/dockertest/v3 v3.9.1/go.mod h1:42Ir9hmvaAPm0Mgibk6mBPi7SFvTXxEcnztDYOJ//uM=
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
@@ -838,6 +835,7 @@ github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dms
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
+github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/securego/gosec/v2 v2.9.1/go.mod h1:oDcDLcatOJxkCGaCaq8lua1jTnYf6Sou4wdiJ1n4iHc=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
@@ -925,10 +923,7 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1
github.com/tomarrell/wrapcheck/v2 v2.4.0/go.mod h1:68bQ/eJg55BROaRTbMjC7vuhL2OgfoG8bLp9ZyoBfyY=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
-github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
-github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
-github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
@@ -962,8 +957,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
-github.com/zsais/go-gin-prometheus v0.1.0 h1:bkLv1XCdzqVgQ36ScgRi09MA2UC1t3tAB6nsfErsGO4=
-github.com/zsais/go-gin-prometheus v0.1.0/go.mod h1:Slirjzuz8uM8Cw0jmPNqbneoqcUtY2GGjn2bEd4NRLY=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k=
@@ -1245,6 +1238,8 @@ golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/grpcv1.go b/grpcv1.go
index 1c891b94..b1e5c1ee 100644
--- a/grpcv1.go
+++ b/grpcv1.go
@@ -3,6 +3,7 @@ package headscale
import (
"context"
+ "fmt"
"strings"
"time"
@@ -195,13 +196,11 @@ func (api headscaleV1APIServer) SetTags(
}
for _, tag := range request.GetTags() {
- if strings.Index(tag, "tag:") != 0 {
+ err := validateTag(tag)
+ if err != nil {
return &v1.SetTagsResponse{
Machine: nil,
- }, status.Error(
- codes.InvalidArgument,
- "Invalid tag detected. Each tag must start with the string 'tag:'",
- )
+ }, status.Error(codes.InvalidArgument, err.Error())
}
}
@@ -220,6 +219,19 @@ func (api headscaleV1APIServer) SetTags(
return &v1.SetTagsResponse{Machine: machine.toProto()}, nil
}
+func validateTag(tag string) error {
+ if strings.Index(tag, "tag:") != 0 {
+ return fmt.Errorf("tag must start with the string 'tag:'")
+ }
+ if strings.ToLower(tag) != tag {
+ return fmt.Errorf("tag should be lowercase")
+ }
+ if len(strings.Fields(tag)) > 1 {
+ return fmt.Errorf("tag should not contains space")
+ }
+ return nil
+}
+
func (api headscaleV1APIServer) DeleteMachine(
ctx context.Context,
request *v1.DeleteMachineRequest,
diff --git a/grpcv1_test.go b/grpcv1_test.go
new file mode 100644
index 00000000..e48ae1ef
--- /dev/null
+++ b/grpcv1_test.go
@@ -0,0 +1,42 @@
+package headscale
+
+import "testing"
+
+func Test_validateTag(t *testing.T) {
+ type args struct {
+ tag string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {
+ name: "valid tag",
+ args: args{tag: "tag:test"},
+ wantErr: false,
+ },
+ {
+ name: "tag without tag prefix",
+ args: args{tag: "test"},
+ wantErr: true,
+ },
+ {
+ name: "uppercase tag",
+ args: args{tag: "tag:tEST"},
+ wantErr: true,
+ },
+ {
+ name: "tag that contains space",
+ args: args{tag: "tag:this is a spaced tag"},
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := validateTag(tt.args.tag); (err != nil) != tt.wantErr {
+ t.Errorf("validateTag() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ })
+ }
+}
diff --git a/integration_cli_test.go b/integration_cli_test.go
index 8ac6ee4d..2f58e71d 100644
--- a/integration_cli_test.go
+++ b/integration_cli_test.go
@@ -40,13 +40,13 @@ func (s *IntegrationCLITestSuite) SetupTest() {
if ppool, err := dockertest.NewPool(""); err == nil {
s.pool = *ppool
} else {
- log.Fatalf("Could not connect to docker: %s", err)
+ s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "")
}
if pnetwork, err := s.pool.CreateNetwork("headscale-test"); err == nil {
s.network = *pnetwork
} else {
- log.Fatalf("Could not create network: %s", err)
+ s.FailNow(fmt.Sprintf("Could not create network: %s", err), "")
}
headscaleBuildOptions := &dockertest.BuildOptions{
@@ -56,7 +56,7 @@ func (s *IntegrationCLITestSuite) SetupTest() {
currentPath, err := os.Getwd()
if err != nil {
- log.Fatalf("Could not determine current path: %s", err)
+ s.FailNow(fmt.Sprintf("Could not determine current path: %s", err), "")
}
headscaleOptions := &dockertest.RunOptions{
@@ -68,11 +68,16 @@ func (s *IntegrationCLITestSuite) SetupTest() {
Cmd: []string{"headscale", "serve"},
}
+ err = s.pool.RemoveContainerByName(headscaleHostname)
+ if err != nil {
+ s.FailNow(fmt.Sprintf("Could not remove existing container before building test: %s", err), "")
+ }
+
fmt.Println("Creating headscale container")
if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil {
s.headscale = *pheadscale
} else {
- log.Fatalf("Could not start headscale container: %s", err)
+ s.FailNow(fmt.Sprintf("Could not start headscale container: %s", err), "")
}
fmt.Println("Created headscale container")
@@ -620,7 +625,7 @@ func (s *IntegrationCLITestSuite) TestNodeTagCommand() {
var errorOutput errOutput
err = json.Unmarshal([]byte(wrongTagResult), &errorOutput)
assert.Nil(s.T(), err)
- assert.Contains(s.T(), errorOutput.Error, "Invalid tag detected")
+ assert.Contains(s.T(), errorOutput.Error, "tag must start with the string 'tag:'")
// Test list all nodes after added seconds
listAllResult, err := ExecuteCommand(
diff --git a/integration_common_test.go b/integration_common_test.go
index f1c4e868..4ee2d3b3 100644
--- a/integration_common_test.go
+++ b/integration_common_test.go
@@ -6,7 +6,10 @@ package headscale
import (
"bytes"
"encoding/json"
+ "errors"
"fmt"
+ "os"
+ "strconv"
"strings"
"time"
@@ -16,9 +19,13 @@ import (
"inet.af/netaddr"
)
-const DOCKER_EXECUTE_TIMEOUT = 10 * time.Second
+const (
+ DOCKER_EXECUTE_TIMEOUT = 10 * time.Second
+)
var (
+ errEnvVarEmpty = errors.New("getenv: environment variable empty")
+
IpPrefix4 = netaddr.MustParseIPPrefix("100.64.0.0/10")
IpPrefix6 = netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/48")
@@ -283,3 +290,25 @@ func getMagicFQDN(
return hostnames, nil
}
+
+func GetEnvStr(key string) (string, error) {
+ v := os.Getenv(key)
+ if v == "" {
+ return v, errEnvVarEmpty
+ }
+
+ return v, nil
+}
+
+func GetEnvBool(key string) (bool, error) {
+ s, err := GetEnvStr(key)
+ if err != nil {
+ return false, err
+ }
+ v, err := strconv.ParseBool(s)
+ if err != nil {
+ return false, err
+ }
+
+ return v, nil
+}
diff --git a/integration_embedded_derp_test.go b/integration_embedded_derp_test.go
index 5f388694..ecca8ba5 100644
--- a/integration_embedded_derp_test.go
+++ b/integration_embedded_derp_test.go
@@ -40,41 +40,50 @@ type IntegrationDERPTestSuite struct {
pool dockertest.Pool
networks map[int]dockertest.Network // so we keep the containers isolated
headscale dockertest.Resource
+ saveLogs bool
tailscales map[string]dockertest.Resource
joinWaitGroup sync.WaitGroup
}
func TestDERPIntegrationTestSuite(t *testing.T) {
+ saveLogs, err := GetEnvBool("HEADSCALE_INTEGRATION_SAVE_LOG")
+ if err != nil {
+ saveLogs = false
+ }
+
s := new(IntegrationDERPTestSuite)
s.tailscales = make(map[string]dockertest.Resource)
s.networks = make(map[int]dockertest.Network)
+ s.saveLogs = saveLogs
suite.Run(t, s)
// HandleStats, which allows us to check if we passed and save logs
// is called after TearDown, so we cannot tear down containers before
// we have potentially saved the logs.
- for _, tailscale := range s.tailscales {
- if err := s.pool.Purge(&tailscale); err != nil {
+ if s.saveLogs {
+ for _, tailscale := range s.tailscales {
+ if err := s.pool.Purge(&tailscale); err != nil {
+ log.Printf("Could not purge resource: %s\n", err)
+ }
+ }
+
+ if !s.stats.Passed() {
+ err := s.saveLog(&s.headscale, "test_output")
+ if err != nil {
+ log.Printf("Could not save log: %s\n", err)
+ }
+ }
+ if err := s.pool.Purge(&s.headscale); err != nil {
log.Printf("Could not purge resource: %s\n", err)
}
- }
- if !s.stats.Passed() {
- err := s.saveLog(&s.headscale, "test_output")
- if err != nil {
- log.Printf("Could not save log: %s\n", err)
- }
- }
- if err := s.pool.Purge(&s.headscale); err != nil {
- log.Printf("Could not purge resource: %s\n", err)
- }
-
- for _, network := range s.networks {
- if err := network.Close(); err != nil {
- log.Printf("Could not close network: %s\n", err)
+ for _, network := range s.networks {
+ if err := network.Close(); err != nil {
+ log.Printf("Could not close network: %s\n", err)
+ }
}
}
}
@@ -83,14 +92,14 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
if ppool, err := dockertest.NewPool(""); err == nil {
s.pool = *ppool
} else {
- log.Fatalf("Could not connect to docker: %s", err)
+ s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "")
}
for i := 0; i < totalContainers; i++ {
if pnetwork, err := s.pool.CreateNetwork(fmt.Sprintf("headscale-derp-%d", i)); err == nil {
s.networks[i] = *pnetwork
} else {
- log.Fatalf("Could not create network: %s", err)
+ s.FailNow(fmt.Sprintf("Could not create network: %s", err), "")
}
}
@@ -101,7 +110,7 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
currentPath, err := os.Getwd()
if err != nil {
- log.Fatalf("Could not determine current path: %s", err)
+ s.FailNow(fmt.Sprintf("Could not determine current path: %s", err), "")
}
headscaleOptions := &dockertest.RunOptions{
@@ -120,11 +129,16 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
},
}
+ err = s.pool.RemoveContainerByName(headscaleHostname)
+ if err != nil {
+ s.FailNow(fmt.Sprintf("Could not remove existing container before building test: %s", err), "")
+ }
+
log.Println("Creating headscale container")
if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil {
s.headscale = *pheadscale
} else {
- log.Fatalf("Could not start headscale container: %s", err)
+ s.FailNow(fmt.Sprintf("Could not start headscale container: %s", err), "")
}
log.Println("Created headscale container to test DERP")
@@ -290,6 +304,23 @@ func (s *IntegrationDERPTestSuite) tailscaleContainer(
}
func (s *IntegrationDERPTestSuite) TearDownSuite() {
+ if !s.saveLogs {
+ for _, tailscale := range s.tailscales {
+ if err := s.pool.Purge(&tailscale); err != nil {
+ log.Printf("Could not purge resource: %s\n", err)
+ }
+ }
+
+ if err := s.pool.Purge(&s.headscale); err != nil {
+ log.Printf("Could not purge resource: %s\n", err)
+ }
+
+ for _, network := range s.networks {
+ if err := network.Close(); err != nil {
+ log.Printf("Could not close network: %s\n", err)
+ }
+ }
+ }
}
func (s *IntegrationDERPTestSuite) HandleStats(
diff --git a/integration_test.go b/integration_test.go
index 18f28b28..2214b893 100644
--- a/integration_test.go
+++ b/integration_test.go
@@ -36,6 +36,7 @@ type IntegrationTestSuite struct {
pool dockertest.Pool
network dockertest.Network
headscale dockertest.Resource
+ saveLogs bool
namespaces map[string]TestNamespace
@@ -43,6 +44,11 @@ type IntegrationTestSuite struct {
}
func TestIntegrationTestSuite(t *testing.T) {
+ saveLogs, err := GetEnvBool("HEADSCALE_INTEGRATION_SAVE_LOG")
+ if err != nil {
+ saveLogs = false
+ }
+
s := new(IntegrationTestSuite)
s.namespaces = map[string]TestNamespace{
@@ -55,32 +61,35 @@ func TestIntegrationTestSuite(t *testing.T) {
tailscales: make(map[string]dockertest.Resource),
},
}
+ s.saveLogs = saveLogs
suite.Run(t, s)
// HandleStats, which allows us to check if we passed and save logs
// is called after TearDown, so we cannot tear down containers before
// we have potentially saved the logs.
- for _, scales := range s.namespaces {
- for _, tailscale := range scales.tailscales {
- if err := s.pool.Purge(&tailscale); err != nil {
- log.Printf("Could not purge resource: %s\n", err)
+ if s.saveLogs {
+ for _, scales := range s.namespaces {
+ for _, tailscale := range scales.tailscales {
+ if err := s.pool.Purge(&tailscale); err != nil {
+ log.Printf("Could not purge resource: %s\n", err)
+ }
}
}
- }
- if !s.stats.Passed() {
- err := s.saveLog(&s.headscale, "test_output")
- if err != nil {
- log.Printf("Could not save log: %s\n", err)
+ if !s.stats.Passed() {
+ err := s.saveLog(&s.headscale, "test_output")
+ if err != nil {
+ log.Printf("Could not save log: %s\n", err)
+ }
+ }
+ if err := s.pool.Purge(&s.headscale); err != nil {
+ log.Printf("Could not purge resource: %s\n", err)
}
- }
- if err := s.pool.Purge(&s.headscale); err != nil {
- log.Printf("Could not purge resource: %s\n", err)
- }
- if err := s.network.Close(); err != nil {
- log.Printf("Could not close network: %s\n", err)
+ if err := s.network.Close(); err != nil {
+ log.Printf("Could not close network: %s\n", err)
+ }
}
}
@@ -209,13 +218,13 @@ func (s *IntegrationTestSuite) SetupSuite() {
if ppool, err := dockertest.NewPool(""); err == nil {
s.pool = *ppool
} else {
- log.Fatalf("Could not connect to docker: %s", err)
+ s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "")
}
if pnetwork, err := s.pool.CreateNetwork("headscale-test"); err == nil {
s.network = *pnetwork
} else {
- log.Fatalf("Could not create network: %s", err)
+ s.FailNow(fmt.Sprintf("Could not create network: %s", err), "")
}
headscaleBuildOptions := &dockertest.BuildOptions{
@@ -225,7 +234,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
currentPath, err := os.Getwd()
if err != nil {
- log.Fatalf("Could not determine current path: %s", err)
+ s.FailNow(fmt.Sprintf("Could not determine current path: %s", err), "")
}
headscaleOptions := &dockertest.RunOptions{
@@ -237,11 +246,16 @@ func (s *IntegrationTestSuite) SetupSuite() {
Cmd: []string{"headscale", "serve"},
}
+ err = s.pool.RemoveContainerByName(headscaleHostname)
+ if err != nil {
+ s.FailNow(fmt.Sprintf("Could not remove existing container before building test: %s", err), "")
+ }
+
log.Println("Creating headscale container")
if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil {
s.headscale = *pheadscale
} else {
- log.Fatalf("Could not start headscale container: %s", err)
+ s.FailNow(fmt.Sprintf("Could not start headscale container: %s", err), "")
}
log.Println("Created headscale container")
@@ -338,6 +352,23 @@ func (s *IntegrationTestSuite) SetupSuite() {
}
func (s *IntegrationTestSuite) TearDownSuite() {
+ if !s.saveLogs {
+ for _, scales := range s.namespaces {
+ for _, tailscale := range scales.tailscales {
+ if err := s.pool.Purge(&tailscale); err != nil {
+ log.Printf("Could not purge resource: %s\n", err)
+ }
+ }
+ }
+
+ if err := s.pool.Purge(&s.headscale); err != nil {
+ log.Printf("Could not purge resource: %s\n", err)
+ }
+
+ if err := s.network.Close(); err != nil {
+ log.Printf("Could not close network: %s\n", err)
+ }
+ }
}
func (s *IntegrationTestSuite) HandleStats(
diff --git a/integration_test/etc/alt-config.dump.gold.yaml b/integration_test/etc/alt-config.dump.gold.yaml
index a3d7adb0..e8934230 100644
--- a/integration_test/etc/alt-config.dump.gold.yaml
+++ b/integration_test/etc/alt-config.dump.gold.yaml
@@ -20,6 +20,7 @@ dns_config:
nameservers:
- 1.1.1.1
ephemeral_node_inactivity_timeout: 30m
+node_update_check_interval: 10s
grpc_allow_insecure: false
grpc_listen_addr: :50443
ip_prefixes:
diff --git a/integration_test/etc/alt-config.yaml b/integration_test/etc/alt-config.yaml
index 8de9a828..fa1bfcb3 100644
--- a/integration_test/etc/alt-config.yaml
+++ b/integration_test/etc/alt-config.yaml
@@ -2,6 +2,7 @@ log_level: trace
acl_policy_path: ""
db_type: sqlite3
ephemeral_node_inactivity_timeout: 30m
+node_update_check_interval: 10s
ip_prefixes:
- fd7a:115c:a1e0::/48
- 100.64.0.0/10
diff --git a/integration_test/etc/config.dump.gold.yaml b/integration_test/etc/config.dump.gold.yaml
index 4d03d74e..17bb0ca0 100644
--- a/integration_test/etc/config.dump.gold.yaml
+++ b/integration_test/etc/config.dump.gold.yaml
@@ -20,6 +20,7 @@ dns_config:
nameservers:
- 1.1.1.1
ephemeral_node_inactivity_timeout: 30m
+node_update_check_interval: 10s
grpc_allow_insecure: false
grpc_listen_addr: :50443
ip_prefixes:
diff --git a/integration_test/etc/config.yaml b/integration_test/etc/config.yaml
index f055b4ca..e6b34afa 100644
--- a/integration_test/etc/config.yaml
+++ b/integration_test/etc/config.yaml
@@ -2,6 +2,7 @@ log_level: trace
acl_policy_path: ""
db_type: sqlite3
ephemeral_node_inactivity_timeout: 30m
+node_update_check_interval: 10s
ip_prefixes:
- fd7a:115c:a1e0::/48
- 100.64.0.0/10
diff --git a/integration_test/etc_embedded_derp/config.yaml b/integration_test/etc_embedded_derp/config.yaml
index a8b57af5..e6ad3b00 100644
--- a/integration_test/etc_embedded_derp/config.yaml
+++ b/integration_test/etc_embedded_derp/config.yaml
@@ -2,6 +2,7 @@ log_level: trace
acl_policy_path: ""
db_type: sqlite3
ephemeral_node_inactivity_timeout: 30m
+node_update_check_interval: 10s
ip_prefixes:
- fd7a:115c:a1e0::/48
- 100.64.0.0/10
diff --git a/machine.go b/machine.go
index 1bed2955..dda49020 100644
--- a/machine.go
+++ b/machine.go
@@ -374,7 +374,13 @@ func (h *Headscale) UpdateMachineFromDatabase(machine *Machine) error {
// SetTags takes a Machine struct pointer and update the forced tags.
func (h *Headscale) SetTags(machine *Machine, tags []string) error {
- machine.ForcedTags = tags
+ newTags := []string{}
+ for _, tag := range tags {
+ if !contains(newTags, tag) {
+ newTags = append(newTags, tag)
+ }
+ }
+ machine.ForcedTags = newTags
if err := h.UpdateACLRules(); err != nil && !errors.Is(err, errEmptyPolicy) {
return err
}
diff --git a/machine_test.go b/machine_test.go
index a06d0db2..35c3eed9 100644
--- a/machine_test.go
+++ b/machine_test.go
@@ -280,6 +280,49 @@ func (s *Suite) TestSerdeAddressStrignSlice(c *check.C) {
}
}
+func (s *Suite) TestSetTags(c *check.C) {
+ namespace, err := app.CreateNamespace("test")
+ c.Assert(err, check.IsNil)
+
+ pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
+ c.Assert(err, check.IsNil)
+
+ _, err = app.GetMachine("test", "testmachine")
+ c.Assert(err, check.NotNil)
+
+ machine := &Machine{
+ ID: 0,
+ MachineKey: "foo",
+ NodeKey: "bar",
+ DiscoKey: "faa",
+ Hostname: "testmachine",
+ NamespaceID: namespace.ID,
+ RegisterMethod: RegisterMethodAuthKey,
+ AuthKeyID: uint(pak.ID),
+ }
+ app.db.Save(machine)
+
+ // assign simple tags
+ sTags := []string{"tag:test", "tag:foo"}
+ err = app.SetTags(machine, sTags)
+ c.Assert(err, check.IsNil)
+ machine, err = app.GetMachine("test", "testmachine")
+ c.Assert(err, check.IsNil)
+ c.Assert(machine.ForcedTags, check.DeepEquals, StringList(sTags))
+
+ // assign duplicat tags, expect no errors but no doubles in DB
+ eTags := []string{"tag:bar", "tag:test", "tag:unknown", "tag:test"}
+ err = app.SetTags(machine, eTags)
+ c.Assert(err, check.IsNil)
+ machine, err = app.GetMachine("test", "testmachine")
+ c.Assert(err, check.IsNil)
+ c.Assert(
+ machine.ForcedTags,
+ check.DeepEquals,
+ StringList([]string{"tag:bar", "tag:test", "tag:unknown"}),
+ )
+}
+
func Test_getTags(t *testing.T) {
type args struct {
aclPolicy *ACLPolicy
diff --git a/poll.go b/poll.go
index 9218495d..9c17b5cb 100644
--- a/poll.go
+++ b/poll.go
@@ -16,8 +16,7 @@ import (
)
const (
- keepAliveInterval = 60 * time.Second
- updateCheckInterval = 10 * time.Second
+ keepAliveInterval = 60 * time.Second
)
type contextKey string
@@ -291,6 +290,9 @@ func (h *Headscale) PollNetMapStream(
keepAliveChan chan []byte,
updateChan chan struct{},
) {
+ h.pollNetMapStreamWG.Add(1)
+ defer h.pollNetMapStreamWG.Done()
+
ctx := context.WithValue(req.Context(), machineNameContextKey, machine.Hostname)
ctx, cancel := context.WithCancel(ctx)
@@ -640,7 +642,7 @@ func (h *Headscale) scheduledPollWorker(
machine *Machine,
) {
keepAliveTicker := time.NewTicker(keepAliveInterval)
- updateCheckerTicker := time.NewTicker(updateCheckInterval)
+ updateCheckerTicker := time.NewTicker(h.cfg.NodeUpdateCheckInterval)
defer closeChanWithLog(
updateChan,
|