From 5be02ee6f858416c2c494472069029c198258b32 Mon Sep 17 00:00:00 2001 From: Tom Proctor Date: Thu, 8 Jan 2026 14:07:51 +0000 Subject: [PATCH] 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 --- cmd/k8s-operator/e2e/setup.go | 81 +++++++++++++++++++++-------------- flake.nix | 2 +- go.mod | 1 - go.mod.sri | 2 +- go.sum | 2 - shell.nix | 2 +- 6 files changed, 53 insertions(+), 37 deletions(-) diff --git a/cmd/k8s-operator/e2e/setup.go b/cmd/k8s-operator/e2e/setup.go index 287ef4969..00e75ddd5 100644 --- a/cmd/k8s-operator/e2e/setup.go +++ b/cmd/k8s-operator/e2e/setup.go @@ -4,12 +4,13 @@ package e2e import ( + "bytes" "context" "crypto/rand" "crypto/tls" "crypto/x509" _ "embed" - jsonv1 "encoding/json" + "encoding/json" "flag" "fmt" "io" @@ -51,7 +52,7 @@ import ( "sigs.k8s.io/kind/pkg/cluster" "sigs.k8s.io/kind/pkg/cluster/nodeutils" "sigs.k8s.io/kind/pkg/cmd" - "tailscale.com/client/tailscale/v2" + "tailscale.com/internal/client/tailscale" "tailscale.com/ipn" "tailscale.com/ipn/store/mem" tsapi "tailscale.com/k8s-operator/apis/v1alpha1" @@ -66,9 +67,9 @@ const ( ) var ( - tsClient = &tailscale.Client{Tailnet: "-"} // For API calls to control. - tnClient *tsnet.Server // For testing real tailnet traffic. - kubeClient client.WithWatch // For k8s API calls. + tsClient *tailscale.Client // For API calls to control. + tnClient *tsnet.Server // For testing real tailnet traffic. + kubeClient client.WithWatch // For k8s API calls. //go:embed certs/pebble.minica.crt pebbleMiniCACert []byte @@ -241,7 +242,7 @@ func runTests(m *testing.M) (int, error) { var apiKeyData struct { 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) } if apiKeyData.APIKey == "" { @@ -249,28 +250,48 @@ func runTests(m *testing.M) (int, error) { } // Finish setting up tsClient. - baseURL, err := url.Parse("http://localhost:31544") - if err != nil { - return 0, fmt.Errorf("parse url: %w", err) - } - tsClient.BaseURL = baseURL - tsClient.APIKey = apiKeyData.APIKey - tsClient.HTTP = &http.Client{} + tsClient = tailscale.NewClient("-", tailscale.APIKey(apiKeyData.APIKey)) + tsClient.BaseURL = "http://localhost:31544" // 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) } + 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") - key, err := tsClient.Keys().CreateOAuthClient(ctx, tailscale.CreateOAuthClientRequest{ - Scopes: []string{"auth_keys", "devices:core", "services"}, - Tags: []string{"tag:k8s-operator"}, - Description: "k8s-operator client for e2e tests", + reqBody, err := json.Marshal(map[string]any{ + "keyType": "client", + "scopes": []string{"auth_keys", "devices:core", "services"}, + "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 { 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 clientSecret = key.Key } else { @@ -290,12 +311,14 @@ func runTests(m *testing.M) (int, error) { TokenURL: fmt.Sprintf("%s/api/v2/oauth/token", ipn.DefaultControlURL), Scopes: []string{"auth_keys"}, } - baseURL, _ := url.Parse(ipn.DefaultControlURL) - tsClient = &tailscale.Client{ - Tailnet: "-", - HTTP: credentials.Client(ctx), - BaseURL: baseURL, + tk, err := credentials.Token(ctx) + if err != nil { + return 0, fmt.Errorf("failed to get OAuth token: %w", err) } + // 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 @@ -422,22 +445,18 @@ func runTests(m *testing.M) (int, error) { caps.Devices.Create.Ephemeral = true caps.Devices.Create.Tags = []string{"tag:k8s"} - authKey, err := tsClient.Keys().CreateAuthKey(ctx, tailscale.CreateKeyRequest{ - Capabilities: caps, - ExpirySeconds: 600, - Description: "e2e test authkey", - }) + authKey, authKeyMeta, err := tsClient.CreateKey(ctx, caps) if err != nil { return 0, err } - defer tsClient.Keys().Delete(context.Background(), authKey.ID) + defer tsClient.DeleteKey(context.Background(), authKeyMeta.ID) tnClient = &tsnet.Server{ - ControlURL: tsClient.BaseURL.String(), + ControlURL: tsClient.BaseURL, Hostname: "test-proxy", Ephemeral: true, Store: &mem.Store{}, - AuthKey: authKey.Key, + AuthKey: authKey, } _, err = tnClient.Up(ctx) if err != nil { diff --git a/flake.nix b/flake.nix index dff1f9e90..68aaa15e9 100644 --- a/flake.nix +++ b/flake.nix @@ -151,4 +151,4 @@ }); }; } -# nix-direnv cache busting line: sha256-7Ak8bu6uQV+XmjzgW7yqFdptqocWYJS6grkCUAr1qlo= +# nix-direnv cache busting line: sha256-kXdjsA1QIXS13vMBTMbxBJK4tewd6rVz0Csod+HtN10= diff --git a/go.mod b/go.mod index 1d018598d..3b3df7554 100644 --- a/go.mod +++ b/go.mod @@ -129,7 +129,6 @@ require ( sigs.k8s.io/kind v0.30.0 sigs.k8s.io/yaml v1.6.0 software.sslmate.com/src/go-pkcs12 v0.4.0 - tailscale.com/client/tailscale/v2 v2.0.0-20250925170215-115deaf34058 ) require ( diff --git a/go.mod.sri b/go.mod.sri index 898bc8cc8..0ac77cb8b 100644 --- a/go.mod.sri +++ b/go.mod.sri @@ -1 +1 @@ -sha256-7Ak8bu6uQV+XmjzgW7yqFdptqocWYJS6grkCUAr1qlo= +sha256-kXdjsA1QIXS13vMBTMbxBJK4tewd6rVz0Csod+HtN10= diff --git a/go.sum b/go.sum index af9bca25f..4e2895d9e 100644 --- a/go.sum +++ b/go.sum @@ -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= 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= -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= diff --git a/shell.nix b/shell.nix index 20c6af763..4f2d59851 100644 --- a/shell.nix +++ b/shell.nix @@ -16,4 +16,4 @@ ) { src = ./.; }).shellNix -# nix-direnv cache busting line: sha256-7Ak8bu6uQV+XmjzgW7yqFdptqocWYJS6grkCUAr1qlo= +# nix-direnv cache busting line: sha256-kXdjsA1QIXS13vMBTMbxBJK4tewd6rVz0Csod+HtN10=