cmd/k8s-operator/e2e,go.mod: remove client v2 dependency

It's not worth adding the v2 client just for these e2e tests. Remove
that dependency for now to keep a clear separation, but we should revive
the v2 client version if we ever decide to take that dependency for the
tailscale/tailscale repo as a whole.

Updates tailscale/corp#32085

Change-Id: Ic51ce233d5f14ce2d25f31a6c4bb9cf545057dd0
Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
This commit is contained in:
Tom Proctor
2026-01-08 14:07:51 +00:00
parent 73cb3b491e
commit 5be02ee6f8
6 changed files with 53 additions and 37 deletions
+50 -31
View File
@@ -4,12 +4,13 @@
package e2e package e2e
import ( import (
"bytes"
"context" "context"
"crypto/rand" "crypto/rand"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
_ "embed" _ "embed"
jsonv1 "encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"io" "io"
@@ -51,7 +52,7 @@ import (
"sigs.k8s.io/kind/pkg/cluster" "sigs.k8s.io/kind/pkg/cluster"
"sigs.k8s.io/kind/pkg/cluster/nodeutils" "sigs.k8s.io/kind/pkg/cluster/nodeutils"
"sigs.k8s.io/kind/pkg/cmd" "sigs.k8s.io/kind/pkg/cmd"
"tailscale.com/client/tailscale/v2" "tailscale.com/internal/client/tailscale"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/ipn/store/mem" "tailscale.com/ipn/store/mem"
tsapi "tailscale.com/k8s-operator/apis/v1alpha1" tsapi "tailscale.com/k8s-operator/apis/v1alpha1"
@@ -66,9 +67,9 @@ const (
) )
var ( var (
tsClient = &tailscale.Client{Tailnet: "-"} // For API calls to control. tsClient *tailscale.Client // For API calls to control.
tnClient *tsnet.Server // For testing real tailnet traffic. tnClient *tsnet.Server // For testing real tailnet traffic.
kubeClient client.WithWatch // For k8s API calls. kubeClient client.WithWatch // For k8s API calls.
//go:embed certs/pebble.minica.crt //go:embed certs/pebble.minica.crt
pebbleMiniCACert []byte pebbleMiniCACert []byte
@@ -241,7 +242,7 @@ func runTests(m *testing.M) (int, error) {
var apiKeyData struct { var apiKeyData struct {
APIKey string `json:"apiKey"` APIKey string `json:"apiKey"`
} }
if err := jsonv1.Unmarshal(b, &apiKeyData); err != nil { if err := json.Unmarshal(b, &apiKeyData); err != nil {
return 0, fmt.Errorf("failed to parse api-key.json: %w", err) return 0, fmt.Errorf("failed to parse api-key.json: %w", err)
} }
if apiKeyData.APIKey == "" { if apiKeyData.APIKey == "" {
@@ -249,28 +250,48 @@ func runTests(m *testing.M) (int, error) {
} }
// Finish setting up tsClient. // Finish setting up tsClient.
baseURL, err := url.Parse("http://localhost:31544") tsClient = tailscale.NewClient("-", tailscale.APIKey(apiKeyData.APIKey))
if err != nil { tsClient.BaseURL = "http://localhost:31544"
return 0, fmt.Errorf("parse url: %w", err)
}
tsClient.BaseURL = baseURL
tsClient.APIKey = apiKeyData.APIKey
tsClient.HTTP = &http.Client{}
// Set ACLs and create OAuth client. // Set ACLs and create OAuth client.
if err := tsClient.PolicyFile().Set(ctx, string(requiredACLs), ""); err != nil { req, _ := http.NewRequest("POST", tsClient.BuildTailnetURL("acl"), bytes.NewReader(requiredACLs))
resp, err := tsClient.Do(req)
if err != nil {
return 0, fmt.Errorf("failed to set ACLs: %w", err) return 0, fmt.Errorf("failed to set ACLs: %w", err)
} }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
b, _ := io.ReadAll(resp.Body)
return 0, fmt.Errorf("HTTP %d setting ACLs: %s", resp.StatusCode, string(b))
}
logger.Infof("ACLs configured") logger.Infof("ACLs configured")
key, err := tsClient.Keys().CreateOAuthClient(ctx, tailscale.CreateOAuthClientRequest{ reqBody, err := json.Marshal(map[string]any{
Scopes: []string{"auth_keys", "devices:core", "services"}, "keyType": "client",
Tags: []string{"tag:k8s-operator"}, "scopes": []string{"auth_keys", "devices:core", "services"},
Description: "k8s-operator client for e2e tests", "tags": []string{"tag:k8s-operator"},
"description": "k8s-operator client for e2e tests",
}) })
if err != nil {
return 0, fmt.Errorf("failed to marshal OAuth client creation request: %w", err)
}
req, _ = http.NewRequest("POST", tsClient.BuildTailnetURL("keys"), bytes.NewReader(reqBody))
resp, err = tsClient.Do(req)
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to create OAuth client: %w", err) return 0, fmt.Errorf("failed to create OAuth client: %w", err)
} }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
b, _ := io.ReadAll(resp.Body)
return 0, fmt.Errorf("HTTP %d creating OAuth client: %s", resp.StatusCode, string(b))
}
var key struct {
ID string `json:"id"`
Key string `json:"key"`
}
if err := json.NewDecoder(resp.Body).Decode(&key); err != nil {
return 0, fmt.Errorf("failed to decode OAuth client creation response: %w", err)
}
clientID = key.ID clientID = key.ID
clientSecret = key.Key clientSecret = key.Key
} else { } else {
@@ -290,12 +311,14 @@ func runTests(m *testing.M) (int, error) {
TokenURL: fmt.Sprintf("%s/api/v2/oauth/token", ipn.DefaultControlURL), TokenURL: fmt.Sprintf("%s/api/v2/oauth/token", ipn.DefaultControlURL),
Scopes: []string{"auth_keys"}, Scopes: []string{"auth_keys"},
} }
baseURL, _ := url.Parse(ipn.DefaultControlURL) tk, err := credentials.Token(ctx)
tsClient = &tailscale.Client{ if err != nil {
Tailnet: "-", return 0, fmt.Errorf("failed to get OAuth token: %w", err)
HTTP: credentials.Client(ctx),
BaseURL: baseURL,
} }
// An access token will last for an hour which is plenty of time for
// the tests to run. No need for token refresh logic.
tsClient = tailscale.NewClient("-", tailscale.APIKey(tk.AccessToken))
tsClient.BaseURL = "http://localhost:31544"
} }
var ossTag string var ossTag string
@@ -422,22 +445,18 @@ func runTests(m *testing.M) (int, error) {
caps.Devices.Create.Ephemeral = true caps.Devices.Create.Ephemeral = true
caps.Devices.Create.Tags = []string{"tag:k8s"} caps.Devices.Create.Tags = []string{"tag:k8s"}
authKey, err := tsClient.Keys().CreateAuthKey(ctx, tailscale.CreateKeyRequest{ authKey, authKeyMeta, err := tsClient.CreateKey(ctx, caps)
Capabilities: caps,
ExpirySeconds: 600,
Description: "e2e test authkey",
})
if err != nil { if err != nil {
return 0, err return 0, err
} }
defer tsClient.Keys().Delete(context.Background(), authKey.ID) defer tsClient.DeleteKey(context.Background(), authKeyMeta.ID)
tnClient = &tsnet.Server{ tnClient = &tsnet.Server{
ControlURL: tsClient.BaseURL.String(), ControlURL: tsClient.BaseURL,
Hostname: "test-proxy", Hostname: "test-proxy",
Ephemeral: true, Ephemeral: true,
Store: &mem.Store{}, Store: &mem.Store{},
AuthKey: authKey.Key, AuthKey: authKey,
} }
_, err = tnClient.Up(ctx) _, err = tnClient.Up(ctx)
if err != nil { if err != nil {
+1 -1
View File
@@ -151,4 +151,4 @@
}); });
}; };
} }
# nix-direnv cache busting line: sha256-7Ak8bu6uQV+XmjzgW7yqFdptqocWYJS6grkCUAr1qlo= # nix-direnv cache busting line: sha256-kXdjsA1QIXS13vMBTMbxBJK4tewd6rVz0Csod+HtN10=
-1
View File
@@ -129,7 +129,6 @@ require (
sigs.k8s.io/kind v0.30.0 sigs.k8s.io/kind v0.30.0
sigs.k8s.io/yaml v1.6.0 sigs.k8s.io/yaml v1.6.0
software.sslmate.com/src/go-pkcs12 v0.4.0 software.sslmate.com/src/go-pkcs12 v0.4.0
tailscale.com/client/tailscale/v2 v2.0.0-20250925170215-115deaf34058
) )
require ( require (
+1 -1
View File
@@ -1 +1 @@
sha256-7Ak8bu6uQV+XmjzgW7yqFdptqocWYJS6grkCUAr1qlo= sha256-kXdjsA1QIXS13vMBTMbxBJK4tewd6rVz0Csod+HtN10=
-2
View File
@@ -1739,5 +1739,3 @@ sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
tailscale.com/client/tailscale/v2 v2.0.0-20250925170215-115deaf34058 h1:X78yMWHEQLo0iFspwDpdbfNIfAP8thmIBrplbd3/0lk=
tailscale.com/client/tailscale/v2 v2.0.0-20250925170215-115deaf34058/go.mod h1:RkAl+CyJiu437uUelFWW/2wL+EgZ6Vd15S1f+IitGr4=
+1 -1
View File
@@ -16,4 +16,4 @@
) { ) {
src = ./.; src = ./.;
}).shellNix }).shellNix
# nix-direnv cache busting line: sha256-7Ak8bu6uQV+XmjzgW7yqFdptqocWYJS6grkCUAr1qlo= # nix-direnv cache busting line: sha256-kXdjsA1QIXS13vMBTMbxBJK4tewd6rVz0Csod+HtN10=