cmd/tailscale/cli: make configure kubeconfig accept Tailscale Services (#16601)

The Kubernetes API server proxy is getting the ability to serve on a
Tailscale Service instead of individual node names. Update the configure
kubeconfig sub-command to accept arguments that look like a Tailscale
Service. Note, we can't know for sure whether a peer is advertising a
Tailscale Service, we can only guess based on the ExtraRecords in the
netmap and that IP showing up in a peer's AllowedIPs.

Also adds an --http flag to allow targeting individual proxies that can
be adverting on http for their node name, and makes the command a bit
more forgiving on the range of inputs it accepts and how eager it is to
print the help text when the input is obviously wrong.

Updates #13358

Change-Id: Ica0509c6b2c707252a43d7c18b530ec1acf7508f

Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
This commit is contained in:
Tom Proctor
2025-07-22 10:07:09 +01:00
committed by GitHub
parent 8453170aa1
commit 6f7e78b10f
2 changed files with 194 additions and 13 deletions
+55 -1
View File
@@ -6,6 +6,7 @@ package cli
import (
"bytes"
"fmt"
"strings"
"testing"
@@ -16,6 +17,7 @@ func TestKubeconfig(t *testing.T) {
const fqdn = "foo.tail-scale.ts.net"
tests := []struct {
name string
http bool
in string
want string
wantErr error
@@ -48,6 +50,27 @@ contexts:
current-context: foo.tail-scale.ts.net
kind: Config
users:
- name: tailscale-auth
user:
token: unused`,
},
{
name: "empty_http",
http: true,
in: "",
want: `apiVersion: v1
clusters:
- cluster:
server: http://foo.tail-scale.ts.net
name: foo.tail-scale.ts.net
contexts:
- context:
cluster: foo.tail-scale.ts.net
user: tailscale-auth
name: foo.tail-scale.ts.net
current-context: foo.tail-scale.ts.net
kind: Config
users:
- name: tailscale-auth
user:
token: unused`,
@@ -202,7 +225,11 @@ users:
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := updateKubeconfig([]byte(tt.in), fqdn)
scheme := "https://"
if tt.http {
scheme = "http://"
}
got, err := updateKubeconfig([]byte(tt.in), scheme, fqdn)
if err != nil {
if err != tt.wantErr {
t.Fatalf("updateKubeconfig() error = %v, wantErr %v", err, tt.wantErr)
@@ -219,3 +246,30 @@ users:
})
}
}
func TestGetInputs(t *testing.T) {
for _, arg := range []string{
"foo.tail-scale.ts.net",
"foo",
"127.0.0.1",
} {
for _, prefix := range []string{"", "https://", "http://"} {
for _, httpFlag := range []bool{false, true} {
expectedHost := arg
expectedHTTP := (httpFlag && !strings.HasPrefix(prefix, "https://")) || strings.HasPrefix(prefix, "http://")
t.Run(fmt.Sprintf("%s%s_http=%v", prefix, arg, httpFlag), func(t *testing.T) {
host, http, err := getInputs(prefix+arg, httpFlag)
if err != nil {
t.Fatal(err)
}
if host != expectedHost {
t.Errorf("host = %v, want %v", host, expectedHost)
}
if http != expectedHTTP {
t.Errorf("http = %v, want %v", http, expectedHTTP)
}
})
}
}
}
}