Merge branch 'main' into android-readme

This commit is contained in:
Juan Font 2022-08-09 15:37:20 +02:00 committed by GitHub
commit 913a94d2ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 126 additions and 132 deletions

View file

@ -70,7 +70,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: bufbuild/buf-setup-action@v0.7.0 - uses: bufbuild/buf-setup-action@v1.7.0
- uses: bufbuild/buf-lint-action@v1 - uses: bufbuild/buf-lint-action@v1
with: with:
input: "proto" input: "proto"

View file

@ -11,6 +11,11 @@ jobs:
with: with:
fetch-depth: 2 fetch-depth: 2
- name: Set Swap Space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 10
- name: Get changed files - name: Get changed files
id: changed-files id: changed-files
uses: tj-actions/changed-files@v14.1 uses: tj-actions/changed-files@v14.1
@ -25,11 +30,29 @@ jobs:
- uses: cachix/install-nix-action@v16 - uses: cachix/install-nix-action@v16
if: steps.changed-files.outputs.any_changed == 'true' if: steps.changed-files.outputs.any_changed == 'true'
- name: Run Integration tests - name: Run CLI integration tests
if: steps.changed-files.outputs.any_changed == 'true' if: steps.changed-files.outputs.any_changed == 'true'
uses: nick-fields/retry@v2 uses: nick-fields/retry@v2
with: with:
timeout_minutes: 240 timeout_minutes: 240
max_attempts: 5 max_attempts: 5
retry_on: error retry_on: error
command: nix develop --command -- make test_integration command: nix develop --command -- make test_integration_cli
- name: Run Embedded DERP server integration tests
if: steps.changed-files.outputs.any_changed == 'true'
uses: nick-fields/retry@v2
with:
timeout_minutes: 240
max_attempts: 5
retry_on: error
command: nix develop --command -- make test_integration_derp
- name: Run general integration tests
if: steps.changed-files.outputs.any_changed == 'true'
uses: nick-fields/retry@v2
with:
timeout_minutes: 240
max_attempts: 5
retry_on: error
command: nix develop --command -- make test_integration_general

View file

@ -1,5 +1,5 @@
# Builder image # Builder image
FROM --platform=$BUILDPLATFORM docker.io/golang:1.18.0-bullseye AS build FROM docker.io/golang:1.18.0-bullseye AS build
ARG VERSION=dev ARG VERSION=dev
ENV GOPATH /go ENV GOPATH /go
WORKDIR /go/src/headscale WORKDIR /go/src/headscale
@ -8,8 +8,9 @@ COPY go.mod go.sum /go/src/headscale/
RUN go mod download RUN go mod download
COPY . . COPY . .
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 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
RUN test -e /go/bin/headscale RUN test -e /go/bin/headscale
# Production image # Production image

View file

@ -1,5 +1,5 @@
# Builder image # Builder image
FROM --platform=$BUILDPLATFORM docker.io/golang:1.18.0-alpine AS build FROM docker.io/golang:1.18.0-alpine AS build
ARG VERSION=dev ARG VERSION=dev
ENV GOPATH /go ENV GOPATH /go
WORKDIR /go/src/headscale WORKDIR /go/src/headscale
@ -10,8 +10,8 @@ RUN go mod download
COPY . . 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 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 strip /go/bin/headscale
RUN test -e /go/bin/headscale RUN test -e /go/bin/headscale
# Production image # Production image

View file

@ -1,5 +1,5 @@
# Builder image # Builder image
FROM --platform=$BUILDPLATFORM docker.io/golang:1.18.0-bullseye AS build FROM docker.io/golang:1.18.0-bullseye AS build
ARG VERSION=dev ARG VERSION=dev
ENV GOPATH /go ENV GOPATH /go
WORKDIR /go/src/headscale WORKDIR /go/src/headscale
@ -9,7 +9,6 @@ RUN go mod download
COPY . . 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 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 RUN test -e /go/bin/headscale

View file

@ -24,14 +24,16 @@ dev: lint test build
test: test:
@go test -coverprofile=coverage.out ./... @go test -coverprofile=coverage.out ./...
test_integration: test_integration: test_integration_cli test_integration_derp test_integration_general
go test -failfast -tags integration -timeout 30m -count=1 ./...
test_integration_cli: test_integration_cli:
go test -tags integration -v integration_cli_test.go integration_common_test.go go test -failfast -tags integration_cli,integration -timeout 30m -count=1 ./...
test_integration_derp: test_integration_derp:
go test -tags integration -v integration_embedded_derp_test.go integration_common_test.go go test -failfast -tags integration_derp,integration -timeout 30m -count=1 ./...
test_integration_general:
go test -failfast -tags integration_general,integration -timeout 30m -count=1 ./...
coverprofile_func: coverprofile_func:
go tool cover -func=coverage.out go tool cover -func=coverage.out

View file

@ -14,7 +14,7 @@ const (
apiPrefixLength = 7 apiPrefixLength = 7
apiKeyLength = 32 apiKeyLength = 32
errAPIKeyFailedToParse = Error("Failed to parse ApiKey") ErrAPIKeyFailedToParse = Error("Failed to parse ApiKey")
) )
// APIKey describes the datamodel for API keys used to remotely authenticate with // APIKey describes the datamodel for API keys used to remotely authenticate with
@ -116,7 +116,7 @@ func (h *Headscale) ExpireAPIKey(key *APIKey) error {
func (h *Headscale) ValidateAPIKey(keyStr string) (bool, error) { func (h *Headscale) ValidateAPIKey(keyStr string) (bool, error) {
prefix, hash, found := strings.Cut(keyStr, ".") prefix, hash, found := strings.Cut(keyStr, ".")
if !found { if !found {
return false, errAPIKeyFailedToParse return false, ErrAPIKeyFailedToParse
} }
key, err := h.GetAPIKey(prefix) key, err := h.GetAPIKey(prefix)

6
db.go
View file

@ -248,7 +248,7 @@ func (hi *HostInfo) Scan(destination interface{}) error {
return json.Unmarshal([]byte(value), hi) return json.Unmarshal([]byte(value), hi)
default: default:
return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) return fmt.Errorf("%w: unexpected data type %T", ErrMachineAddressesInvalid, destination)
} }
} }
@ -270,7 +270,7 @@ func (i *IPPrefixes) Scan(destination interface{}) error {
return json.Unmarshal([]byte(value), i) return json.Unmarshal([]byte(value), i)
default: default:
return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) return fmt.Errorf("%w: unexpected data type %T", ErrMachineAddressesInvalid, destination)
} }
} }
@ -292,7 +292,7 @@ func (i *StringList) Scan(destination interface{}) error {
return json.Unmarshal([]byte(value), i) return json.Unmarshal([]byte(value), i)
default: default:
return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) return fmt.Errorf("%w: unexpected data type %T", ErrMachineAddressesInvalid, destination)
} }
} }

View file

@ -36,7 +36,7 @@ ACLs could be written either on [huJSON](https://github.com/tailscale/hujson)
or YAML. Check the [test ACLs](../tests/acls) for further information. or YAML. Check the [test ACLs](../tests/acls) for further information.
When registering the servers we will need to add the flag When registering the servers we will need to add the flag
`--advertised-tags=tag:<tag1>,tag:<tag2>`, and the user (namespace) that is `--advertise-tags=tag:<tag1>,tag:<tag2>`, and the user (namespace) that is
registering the server should be allowed to do it. Since anyone can add tags to registering the server should be allowed to do it. Since anyone can add tags to
a server they can register, the check of the tags is done on headscale server a server they can register, the check of the tags is done on headscale server
and only valid tags are applied. A tag is valid if the namespace that is and only valid tags are applied. A tag is valid if the namespace that is

View file

@ -1,32 +0,0 @@
# 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 .
```

View file

@ -1,5 +1,4 @@
//go:build integration //go:build integration_cli
// +build integration
package headscale package headscale
@ -73,21 +72,22 @@ func (s *IntegrationCLITestSuite) SetupTest() {
s.FailNow(fmt.Sprintf("Could not remove existing container before building test: %s", err), "") s.FailNow(fmt.Sprintf("Could not remove existing container before building test: %s", err), "")
} }
fmt.Println("Creating headscale container") fmt.Println("Creating headscale container for CLI tests")
if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil { if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil {
s.headscale = *pheadscale s.headscale = *pheadscale
} else { } else {
s.FailNow(fmt.Sprintf("Could not start headscale container: %s", err), "") s.FailNow(fmt.Sprintf("Could not start headscale container: %s", err), "")
} }
fmt.Println("Created headscale container") fmt.Println("Created headscale container for CLI tests")
fmt.Println("Waiting for headscale to be ready") fmt.Println("Waiting for headscale to be ready for CLI tests")
hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8080/tcp")) hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8080/tcp"))
if err := s.pool.Retry(func() error { if err := s.pool.Retry(func() error {
url := fmt.Sprintf("http://%s/health", hostEndpoint) url := fmt.Sprintf("http://%s/health", hostEndpoint)
resp, err := http.Get(url) resp, err := http.Get(url)
if err != nil { if err != nil {
fmt.Printf("headscale for CLI test is not ready: %s\n", err)
return err return err
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
@ -102,7 +102,7 @@ func (s *IntegrationCLITestSuite) SetupTest() {
// https://github.com/stretchr/testify/issues/849 // https://github.com/stretchr/testify/issues/849
return // fmt.Errorf("Could not connect to headscale: %s", err) return // fmt.Errorf("Could not connect to headscale: %s", err)
} }
fmt.Println("headscale container is ready") fmt.Println("headscale container is ready for CLI tests")
} }
func (s *IntegrationCLITestSuite) TearDownTest() { func (s *IntegrationCLITestSuite) TearDownTest() {

View file

@ -1,5 +1,4 @@
//go:build integration //go:build integration
// +build integration
package headscale package headscale
@ -20,6 +19,7 @@ import (
) )
const ( const (
headscaleHostname = "headscale-derp"
DOCKER_EXECUTE_TIMEOUT = 10 * time.Second DOCKER_EXECUTE_TIMEOUT = 10 * time.Second
) )
@ -30,9 +30,10 @@ var (
IpPrefix6 = netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/48") IpPrefix6 = netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/48")
tailscaleVersions = []string{ tailscaleVersions = []string{
"head", // "head",
"unstable", // "unstable",
"1.26.0", "1.28.0",
"1.26.2",
"1.24.2", "1.24.2",
"1.22.2", "1.22.2",
"1.20.4", "1.20.4",

View file

@ -1,4 +1,4 @@
//go:build integration //go:build integration_derp
package headscale package headscale
@ -28,9 +28,8 @@ import (
) )
const ( const (
headscaleHostname = "headscale-derp" namespaceName = "derpnamespace"
namespaceName = "derpnamespace" totalContainers = 3
totalContainers = 3
) )
type IntegrationDERPTestSuite struct { type IntegrationDERPTestSuite struct {
@ -134,15 +133,15 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
s.FailNow(fmt.Sprintf("Could not remove existing container before building test: %s", err), "") s.FailNow(fmt.Sprintf("Could not remove existing container before building test: %s", err), "")
} }
log.Println("Creating headscale container") log.Println("Creating headscale container for DERP integration tests")
if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil { if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil {
s.headscale = *pheadscale s.headscale = *pheadscale
} else { } else {
s.FailNow(fmt.Sprintf("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") log.Println("Created headscale container for embedded DERP tests")
log.Println("Creating tailscale containers") log.Println("Creating tailscale containers for embedded DERP tests")
for i := 0; i < totalContainers; i++ { for i := 0; i < totalContainers; i++ {
version := tailscaleVersions[i%len(tailscaleVersions)] version := tailscaleVersions[i%len(tailscaleVersions)]
@ -154,7 +153,7 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
s.tailscales[hostname] = *container s.tailscales[hostname] = *container
} }
log.Println("Waiting for headscale to be ready") log.Println("Waiting for headscale to be ready for embedded DERP tests")
hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8443/tcp")) hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8443/tcp"))
if err := s.pool.Retry(func() error { if err := s.pool.Retry(func() error {
@ -164,6 +163,7 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
client := &http.Client{Transport: insecureTransport} client := &http.Client{Transport: insecureTransport}
resp, err := client.Get(url) resp, err := client.Get(url)
if err != nil { if err != nil {
fmt.Printf("headscale for embedded DERP tests is not ready: %s\n", err)
return err return err
} }
@ -179,7 +179,7 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
// https://github.com/stretchr/testify/issues/849 // https://github.com/stretchr/testify/issues/849
return // fmt.Errorf("Could not connect to headscale: %s", err) return // fmt.Errorf("Could not connect to headscale: %s", err)
} }
log.Println("headscale container is ready") log.Println("headscale container is ready for embedded DERP tests")
log.Printf("Creating headscale namespace: %s\n", namespaceName) log.Printf("Creating headscale namespace: %s\n", namespaceName)
result, err := ExecuteCommand( result, err := ExecuteCommand(

View file

@ -1,5 +1,4 @@
//go:build integration //go:build integration_general
// +build integration
package headscale package headscale
@ -251,15 +250,15 @@ func (s *IntegrationTestSuite) SetupSuite() {
s.FailNow(fmt.Sprintf("Could not remove existing container before building test: %s", err), "") s.FailNow(fmt.Sprintf("Could not remove existing container before building test: %s", err), "")
} }
log.Println("Creating headscale container") log.Println("Creating headscale container for core integration tests")
if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil { if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil {
s.headscale = *pheadscale s.headscale = *pheadscale
} else { } else {
s.FailNow(fmt.Sprintf("Could not start headscale container: %s", err), "") s.FailNow(fmt.Sprintf("Could not start headscale container for core integration tests: %s", err), "")
} }
log.Println("Created headscale container") log.Println("Created headscale container for core integration tests")
log.Println("Creating tailscale containers") log.Println("Creating tailscale containers for core integration tests")
for namespace, scales := range s.namespaces { for namespace, scales := range s.namespaces {
for i := 0; i < scales.count; i++ { for i := 0; i < scales.count; i++ {
version := tailscaleVersions[i%len(tailscaleVersions)] version := tailscaleVersions[i%len(tailscaleVersions)]
@ -273,7 +272,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
} }
} }
log.Println("Waiting for headscale to be ready") log.Println("Waiting for headscale to be ready for core integration tests")
hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8080/tcp")) hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8080/tcp"))
if err := s.pool.Retry(func() error { if err := s.pool.Retry(func() error {
@ -281,6 +280,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
resp, err := http.Get(url) resp, err := http.Get(url)
if err != nil { if err != nil {
fmt.Printf("headscale for core integration test is not ready: %s\n", err)
return err return err
} }
@ -296,7 +296,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
// https://github.com/stretchr/testify/issues/849 // https://github.com/stretchr/testify/issues/849
return // fmt.Errorf("Could not connect to headscale: %s", err) return // fmt.Errorf("Could not connect to headscale: %s", err)
} }
log.Println("headscale container is ready") log.Println("headscale container is ready for core integration tests")
for namespace, scales := range s.namespaces { for namespace, scales := range s.namespaces {
log.Printf("Creating headscale namespace: %s\n", namespace) log.Printf("Creating headscale namespace: %s\n", namespace)

View file

@ -18,14 +18,14 @@ import (
) )
const ( const (
errMachineNotFound = Error("machine not found") ErrMachineNotFound = Error("machine not found")
errMachineRouteIsNotAvailable = Error("route is not available on machine") ErrMachineRouteIsNotAvailable = Error("route is not available on machine")
errMachineAddressesInvalid = Error("failed to parse machine addresses") ErrMachineAddressesInvalid = Error("failed to parse machine addresses")
errMachineNotFoundRegistrationCache = Error( ErrMachineNotFoundRegistrationCache = Error(
"machine not found in registration cache", "machine not found in registration cache",
) )
errCouldNotConvertMachineInterface = Error("failed to convert machine interface") ErrCouldNotConvertMachineInterface = Error("failed to convert machine interface")
errHostnameTooLong = Error("Hostname too long") ErrHostnameTooLong = Error("Hostname too long")
MachineGivenNameHashLength = 8 MachineGivenNameHashLength = 8
MachineGivenNameTrimSize = 2 MachineGivenNameTrimSize = 2
) )
@ -112,7 +112,7 @@ func (ma *MachineAddresses) Scan(destination interface{}) error {
return nil return nil
default: default:
return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) return fmt.Errorf("%w: unexpected data type %T", ErrMachineAddressesInvalid, destination)
} }
} }
@ -337,7 +337,7 @@ func (h *Headscale) GetMachine(namespace string, name string) (*Machine, error)
} }
} }
return nil, errMachineNotFound return nil, ErrMachineNotFound
} }
// GetMachineByID finds a Machine by ID and returns the Machine struct. // GetMachineByID finds a Machine by ID and returns the Machine struct.
@ -635,7 +635,7 @@ func (machine Machine) toNode(
return nil, fmt.Errorf( return nil, fmt.Errorf(
"hostname %q is too long it cannot except 255 ASCII chars: %w", "hostname %q is too long it cannot except 255 ASCII chars: %w",
hostname, hostname,
errHostnameTooLong, ErrHostnameTooLong,
) )
} }
} else { } else {
@ -785,11 +785,11 @@ func (h *Headscale) RegisterMachineFromAuthCallback(
return machine, err return machine, err
} else { } else {
return nil, errCouldNotConvertMachineInterface return nil, ErrCouldNotConvertMachineInterface
} }
} }
return nil, errMachineNotFoundRegistrationCache return nil, ErrMachineNotFoundRegistrationCache
} }
// RegisterMachine is executed from the CLI to register a new Machine using its MachineKey. // RegisterMachine is executed from the CLI to register a new Machine using its MachineKey.
@ -877,7 +877,7 @@ func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error {
return fmt.Errorf( return fmt.Errorf(
"route (%s) is not available on node %s: %w", "route (%s) is not available on node %s: %w",
machine.Hostname, machine.Hostname,
newRoute, errMachineRouteIsNotAvailable, newRoute, ErrMachineRouteIsNotAvailable,
) )
} }
} }

View file

@ -16,10 +16,10 @@ import (
) )
const ( const (
errNamespaceExists = Error("Namespace already exists") ErrNamespaceExists = Error("Namespace already exists")
errNamespaceNotFound = Error("Namespace not found") ErrNamespaceNotFound = Error("Namespace not found")
errNamespaceNotEmptyOfNodes = Error("Namespace not empty: node(s) found") ErrNamespaceNotEmptyOfNodes = Error("Namespace not empty: node(s) found")
errInvalidNamespaceName = Error("Invalid namespace name") ErrInvalidNamespaceName = Error("Invalid namespace name")
) )
const ( const (
@ -47,7 +47,7 @@ func (h *Headscale) CreateNamespace(name string) (*Namespace, error) {
} }
namespace := Namespace{} namespace := Namespace{}
if err := h.db.Where("name = ?", name).First(&namespace).Error; err == nil { if err := h.db.Where("name = ?", name).First(&namespace).Error; err == nil {
return nil, errNamespaceExists return nil, ErrNamespaceExists
} }
namespace.Name = name namespace.Name = name
if err := h.db.Create(&namespace).Error; err != nil { if err := h.db.Create(&namespace).Error; err != nil {
@ -67,7 +67,7 @@ func (h *Headscale) CreateNamespace(name string) (*Namespace, error) {
func (h *Headscale) DestroyNamespace(name string) error { func (h *Headscale) DestroyNamespace(name string) error {
namespace, err := h.GetNamespace(name) namespace, err := h.GetNamespace(name)
if err != nil { if err != nil {
return errNamespaceNotFound return ErrNamespaceNotFound
} }
machines, err := h.ListMachinesInNamespace(name) machines, err := h.ListMachinesInNamespace(name)
@ -75,7 +75,7 @@ func (h *Headscale) DestroyNamespace(name string) error {
return err return err
} }
if len(machines) > 0 { if len(machines) > 0 {
return errNamespaceNotEmptyOfNodes return ErrNamespaceNotEmptyOfNodes
} }
keys, err := h.ListPreAuthKeys(name) keys, err := h.ListPreAuthKeys(name)
@ -110,9 +110,9 @@ func (h *Headscale) RenameNamespace(oldName, newName string) error {
} }
_, err = h.GetNamespace(newName) _, err = h.GetNamespace(newName)
if err == nil { if err == nil {
return errNamespaceExists return ErrNamespaceExists
} }
if !errors.Is(err, errNamespaceNotFound) { if !errors.Is(err, ErrNamespaceNotFound) {
return err return err
} }
@ -132,7 +132,7 @@ func (h *Headscale) GetNamespace(name string) (*Namespace, error) {
result.Error, result.Error,
gorm.ErrRecordNotFound, gorm.ErrRecordNotFound,
) { ) {
return nil, errNamespaceNotFound return nil, ErrNamespaceNotFound
} }
return &namespace, nil return &namespace, nil
@ -272,7 +272,7 @@ func NormalizeToFQDNRules(name string, stripEmailDomain bool) (string, error) {
return "", fmt.Errorf( return "", fmt.Errorf(
"label %v is more than 63 chars: %w", "label %v is more than 63 chars: %w",
elt, elt,
errInvalidNamespaceName, ErrInvalidNamespaceName,
) )
} }
} }
@ -285,21 +285,21 @@ func CheckForFQDNRules(name string) error {
return fmt.Errorf( return fmt.Errorf(
"DNS segment must not be over 63 chars. %v doesn't comply with this rule: %w", "DNS segment must not be over 63 chars. %v doesn't comply with this rule: %w",
name, name,
errInvalidNamespaceName, ErrInvalidNamespaceName,
) )
} }
if strings.ToLower(name) != name { if strings.ToLower(name) != name {
return fmt.Errorf( return fmt.Errorf(
"DNS segment should be lowercase. %v doesn't comply with this rule: %w", "DNS segment should be lowercase. %v doesn't comply with this rule: %w",
name, name,
errInvalidNamespaceName, ErrInvalidNamespaceName,
) )
} }
if invalidCharsInNamespaceRegex.MatchString(name) { if invalidCharsInNamespaceRegex.MatchString(name) {
return fmt.Errorf( return fmt.Errorf(
"DNS segment should only be composed of lowercase ASCII letters numbers, hyphen and dots. %v doesn't comply with theses rules: %w", "DNS segment should only be composed of lowercase ASCII letters numbers, hyphen and dots. %v doesn't comply with theses rules: %w",
name, name,
errInvalidNamespaceName, ErrInvalidNamespaceName,
) )
} }

View file

@ -26,7 +26,7 @@ func (s *Suite) TestCreateAndDestroyNamespace(c *check.C) {
func (s *Suite) TestDestroyNamespaceErrors(c *check.C) { func (s *Suite) TestDestroyNamespaceErrors(c *check.C) {
err := app.DestroyNamespace("test") err := app.DestroyNamespace("test")
c.Assert(err, check.Equals, errNamespaceNotFound) c.Assert(err, check.Equals, ErrNamespaceNotFound)
namespace, err := app.CreateNamespace("test") namespace, err := app.CreateNamespace("test")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
@ -60,7 +60,7 @@ func (s *Suite) TestDestroyNamespaceErrors(c *check.C) {
app.db.Save(&machine) app.db.Save(&machine)
err = app.DestroyNamespace("test") err = app.DestroyNamespace("test")
c.Assert(err, check.Equals, errNamespaceNotEmptyOfNodes) c.Assert(err, check.Equals, ErrNamespaceNotEmptyOfNodes)
} }
func (s *Suite) TestRenameNamespace(c *check.C) { func (s *Suite) TestRenameNamespace(c *check.C) {
@ -76,20 +76,20 @@ func (s *Suite) TestRenameNamespace(c *check.C) {
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, err = app.GetNamespace("test") _, err = app.GetNamespace("test")
c.Assert(err, check.Equals, errNamespaceNotFound) c.Assert(err, check.Equals, ErrNamespaceNotFound)
_, err = app.GetNamespace("test-renamed") _, err = app.GetNamespace("test-renamed")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
err = app.RenameNamespace("test-does-not-exit", "test") err = app.RenameNamespace("test-does-not-exit", "test")
c.Assert(err, check.Equals, errNamespaceNotFound) c.Assert(err, check.Equals, ErrNamespaceNotFound)
namespaceTest2, err := app.CreateNamespace("test2") namespaceTest2, err := app.CreateNamespace("test2")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(namespaceTest2.Name, check.Equals, "test2") c.Assert(namespaceTest2.Name, check.Equals, "test2")
err = app.RenameNamespace("test2", "test-renamed") err = app.RenameNamespace("test2", "test-renamed")
c.Assert(err, check.Equals, errNamespaceExists) c.Assert(err, check.Equals, ErrNamespaceExists)
} }
func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
@ -402,7 +402,7 @@ func (s *Suite) TestSetMachineNamespace(c *check.C) {
c.Assert(machine.Namespace.Name, check.Equals, newNamespace.Name) c.Assert(machine.Namespace.Name, check.Equals, newNamespace.Name)
err = app.SetMachineNamespace(&machine, "non-existing-namespace") err = app.SetMachineNamespace(&machine, "non-existing-namespace")
c.Assert(err, check.Equals, errNamespaceNotFound) c.Assert(err, check.Equals, ErrNamespaceNotFound)
err = app.SetMachineNamespace(&machine, newNamespace.Name) err = app.SetMachineNamespace(&machine, newNamespace.Name)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)

View file

@ -416,7 +416,7 @@ func (h *Headscale) OIDCCallback(
log.Debug().Msg("Registering new machine after successful callback") log.Debug().Msg("Registering new machine after successful callback")
namespace, err := h.GetNamespace(namespaceName) namespace, err := h.GetNamespace(namespaceName)
if errors.Is(err, errNamespaceNotFound) { if errors.Is(err, ErrNamespaceNotFound) {
namespace, err = h.CreateNamespace(namespaceName) namespace, err = h.CreateNamespace(namespaceName)
if err != nil { if err != nil {

View file

@ -14,10 +14,10 @@ import (
) )
const ( const (
errPreAuthKeyNotFound = Error("AuthKey not found") ErrPreAuthKeyNotFound = Error("AuthKey not found")
errPreAuthKeyExpired = Error("AuthKey expired") ErrPreAuthKeyExpired = Error("AuthKey expired")
errSingleUseAuthKeyHasBeenUsed = Error("AuthKey has already been used") ErrSingleUseAuthKeyHasBeenUsed = Error("AuthKey has already been used")
errNamespaceMismatch = Error("namespace mismatch") ErrNamespaceMismatch = Error("namespace mismatch")
) )
// PreAuthKey describes a pre-authorization key usable in a particular namespace. // PreAuthKey describes a pre-authorization key usable in a particular namespace.
@ -92,7 +92,7 @@ func (h *Headscale) GetPreAuthKey(namespace string, key string) (*PreAuthKey, er
} }
if pak.Namespace.Name != namespace { if pak.Namespace.Name != namespace {
return nil, errNamespaceMismatch return nil, ErrNamespaceMismatch
} }
return pak, nil return pak, nil
@ -135,11 +135,11 @@ func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) {
result.Error, result.Error,
gorm.ErrRecordNotFound, gorm.ErrRecordNotFound,
) { ) {
return nil, errPreAuthKeyNotFound return nil, ErrPreAuthKeyNotFound
} }
if pak.Expiration != nil && pak.Expiration.Before(time.Now()) { if pak.Expiration != nil && pak.Expiration.Before(time.Now()) {
return nil, errPreAuthKeyExpired return nil, ErrPreAuthKeyExpired
} }
if pak.Reusable || pak.Ephemeral { // we don't need to check if has been used before if pak.Reusable || pak.Ephemeral { // we don't need to check if has been used before
@ -152,7 +152,7 @@ func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) {
} }
if len(machines) != 0 || pak.Used { if len(machines) != 0 || pak.Used {
return nil, errSingleUseAuthKeyHasBeenUsed return nil, ErrSingleUseAuthKeyHasBeenUsed
} }
return &pak, nil return &pak, nil

View file

@ -44,13 +44,13 @@ func (*Suite) TestExpiredPreAuthKey(c *check.C) {
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
key, err := app.checkKeyValidity(pak.Key) key, err := app.checkKeyValidity(pak.Key)
c.Assert(err, check.Equals, errPreAuthKeyExpired) c.Assert(err, check.Equals, ErrPreAuthKeyExpired)
c.Assert(key, check.IsNil) c.Assert(key, check.IsNil)
} }
func (*Suite) TestPreAuthKeyDoesNotExist(c *check.C) { func (*Suite) TestPreAuthKeyDoesNotExist(c *check.C) {
key, err := app.checkKeyValidity("potatoKey") key, err := app.checkKeyValidity("potatoKey")
c.Assert(err, check.Equals, errPreAuthKeyNotFound) c.Assert(err, check.Equals, ErrPreAuthKeyNotFound)
c.Assert(key, check.IsNil) c.Assert(key, check.IsNil)
} }
@ -86,7 +86,7 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) {
app.db.Save(&machine) app.db.Save(&machine)
key, err := app.checkKeyValidity(pak.Key) key, err := app.checkKeyValidity(pak.Key)
c.Assert(err, check.Equals, errSingleUseAuthKeyHasBeenUsed) c.Assert(err, check.Equals, ErrSingleUseAuthKeyHasBeenUsed)
c.Assert(key, check.IsNil) c.Assert(key, check.IsNil)
} }
@ -174,7 +174,7 @@ func (*Suite) TestExpirePreauthKey(c *check.C) {
c.Assert(pak.Expiration, check.NotNil) c.Assert(pak.Expiration, check.NotNil)
key, err := app.checkKeyValidity(pak.Key) key, err := app.checkKeyValidity(pak.Key)
c.Assert(err, check.Equals, errPreAuthKeyExpired) c.Assert(err, check.Equals, ErrPreAuthKeyExpired)
c.Assert(key, check.IsNil) c.Assert(key, check.IsNil)
} }
@ -188,5 +188,5 @@ func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) {
app.db.Save(&pak) app.db.Save(&pak)
_, err = app.checkKeyValidity(pak.Key) _, err = app.checkKeyValidity(pak.Key)
c.Assert(err, check.Equals, errSingleUseAuthKeyHasBeenUsed) c.Assert(err, check.Equals, ErrSingleUseAuthKeyHasBeenUsed)
} }

View file

@ -7,7 +7,7 @@ import (
) )
const ( const (
errRouteIsNotAvailable = Error("route is not available") ErrRouteIsNotAvailable = Error("route is not available")
) )
// Deprecated: use machine function instead // Deprecated: use machine function instead
@ -106,7 +106,7 @@ func (h *Headscale) EnableNodeRoute(
} }
if !available { if !available {
return errRouteIsNotAvailable return ErrRouteIsNotAvailable
} }
machine.EnabledRoutes = enabledRoutes machine.EnabledRoutes = enabledRoutes

View file

@ -27,8 +27,8 @@ import (
) )
const ( const (
errCannotDecryptReponse = Error("cannot decrypt response") ErrCannotDecryptResponse = Error("cannot decrypt response")
errCouldNotAllocateIP = Error("could not find any suitable IP") ErrCouldNotAllocateIP = Error("could not find any suitable IP")
// These constants are copied from the upstream tailscale.com/types/key // These constants are copied from the upstream tailscale.com/types/key
// library, because they are not exported. // library, because they are not exported.
@ -120,7 +120,7 @@ func decode(
decrypted, ok := privKey.OpenFrom(*pubKey, msg) decrypted, ok := privKey.OpenFrom(*pubKey, msg)
if !ok { if !ok {
return errCannotDecryptReponse return ErrCannotDecryptResponse
} }
if err := json.Unmarshal(decrypted, output); err != nil { if err := json.Unmarshal(decrypted, output); err != nil {
@ -181,7 +181,7 @@ func (h *Headscale) getAvailableIP(ipPrefix netaddr.IPPrefix) (*netaddr.IP, erro
for { for {
if !ipPrefix.Contains(ip) { if !ipPrefix.Contains(ip) {
return nil, errCouldNotAllocateIP return nil, ErrCouldNotAllocateIP
} }
switch { switch {