268 lines
5.3 KiB
Go
268 lines
5.3 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build linux
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"tailscale.com/client/tailscale"
|
|
"tailscale.com/ipn"
|
|
"tailscale.com/kube/kubetypes"
|
|
)
|
|
|
|
func TestUpdateServeConfig(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
sc *ipn.ServeConfig
|
|
certDomain string
|
|
wantCall bool
|
|
}{
|
|
{
|
|
name: "no_https_no_cert_domain",
|
|
sc: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
80: {HTTP: true},
|
|
},
|
|
},
|
|
certDomain: kubetypes.ValueNoHTTPS, // tailnet has HTTPS disabled
|
|
wantCall: true, // should set serve config as it doesn't have HTTPS endpoints
|
|
},
|
|
{
|
|
name: "https_with_cert_domain",
|
|
sc: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
443: {HTTPS: true},
|
|
},
|
|
Web: map[ipn.HostPort]*ipn.WebServerConfig{
|
|
"${TS_CERT_DOMAIN}:443": {
|
|
Handlers: map[string]*ipn.HTTPHandler{
|
|
"/": {Proxy: "http://10.0.1.100:8080"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
certDomain: "test-node.tailnet.ts.net",
|
|
wantCall: true,
|
|
},
|
|
{
|
|
name: "https_without_cert_domain",
|
|
sc: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
443: {HTTPS: true},
|
|
},
|
|
},
|
|
certDomain: kubetypes.ValueNoHTTPS,
|
|
wantCall: false, // incorrect configuration- should not set serve config
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
fakeLC := &fakeLocalClient{}
|
|
err := updateServeConfig(context.Background(), tt.sc, tt.certDomain, fakeLC)
|
|
if err != nil {
|
|
t.Errorf("updateServeConfig() error = %v", err)
|
|
}
|
|
if fakeLC.setServeCalled != tt.wantCall {
|
|
t.Errorf("SetServeConfig() called = %v, want %v", fakeLC.setServeCalled, tt.wantCall)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReadServeConfig(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
gotSC string
|
|
certDomain string
|
|
wantSC *ipn.ServeConfig
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "empty_file",
|
|
},
|
|
{
|
|
name: "valid_config_with_cert_domain_placeholder",
|
|
gotSC: `{
|
|
"TCP": {
|
|
"443": {
|
|
"HTTPS": true
|
|
}
|
|
},
|
|
"Web": {
|
|
"${TS_CERT_DOMAIN}:443": {
|
|
"Handlers": {
|
|
"/api": {
|
|
"Proxy": "https://10.2.3.4/api"
|
|
}}}}}`,
|
|
certDomain: "example.com",
|
|
wantSC: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
443: {
|
|
HTTPS: true,
|
|
},
|
|
},
|
|
Web: map[ipn.HostPort]*ipn.WebServerConfig{
|
|
ipn.HostPort("example.com:443"): {
|
|
Handlers: map[string]*ipn.HTTPHandler{
|
|
"/api": {
|
|
Proxy: "https://10.2.3.4/api",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "valid_config_for_http_proxy",
|
|
gotSC: `{
|
|
"TCP": {
|
|
"80": {
|
|
"HTTP": true
|
|
}
|
|
}}`,
|
|
wantSC: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
80: {
|
|
HTTP: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "config_without_cert_domain",
|
|
gotSC: `{
|
|
"TCP": {
|
|
"443": {
|
|
"HTTPS": true
|
|
}
|
|
},
|
|
"Web": {
|
|
"localhost:443": {
|
|
"Handlers": {
|
|
"/api": {
|
|
"Proxy": "https://10.2.3.4/api"
|
|
}}}}}`,
|
|
certDomain: "",
|
|
wantErr: false,
|
|
wantSC: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
443: {
|
|
HTTPS: true,
|
|
},
|
|
},
|
|
Web: map[ipn.HostPort]*ipn.WebServerConfig{
|
|
ipn.HostPort("localhost:443"): {
|
|
Handlers: map[string]*ipn.HTTPHandler{
|
|
"/api": {
|
|
Proxy: "https://10.2.3.4/api",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "invalid_json",
|
|
gotSC: "invalid json",
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "serve-config.json")
|
|
if err := os.WriteFile(path, []byte(tt.gotSC), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
got, err := readServeConfig(path, tt.certDomain)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("readServeConfig() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !cmp.Equal(got, tt.wantSC) {
|
|
t.Errorf("readServeConfig() diff (-got +want):\n%s", cmp.Diff(got, tt.wantSC))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type fakeLocalClient struct {
|
|
*tailscale.LocalClient
|
|
setServeCalled bool
|
|
}
|
|
|
|
func (m *fakeLocalClient) SetServeConfig(ctx context.Context, cfg *ipn.ServeConfig) error {
|
|
m.setServeCalled = true
|
|
return nil
|
|
}
|
|
|
|
func TestHasHTTPSEndpoint(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
cfg *ipn.ServeConfig
|
|
want bool
|
|
}{
|
|
{
|
|
name: "nil_config",
|
|
cfg: nil,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "empty_config",
|
|
cfg: &ipn.ServeConfig{},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "no_https_endpoints",
|
|
cfg: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
80: {
|
|
HTTPS: false,
|
|
},
|
|
},
|
|
},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "has_https_endpoint",
|
|
cfg: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
443: {
|
|
HTTPS: true,
|
|
},
|
|
},
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "mixed_endpoints",
|
|
cfg: &ipn.ServeConfig{
|
|
TCP: map[uint16]*ipn.TCPPortHandler{
|
|
80: {HTTPS: false},
|
|
443: {HTTPS: true},
|
|
},
|
|
},
|
|
want: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := hasHTTPSEndpoint(tt.cfg)
|
|
if got != tt.want {
|
|
t.Errorf("hasHTTPSEndpoint() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|