Browse Source

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 <[email protected]>
Tom Proctor 2 months ago
parent
commit
5be02ee6f8
6 changed files with 53 additions and 37 deletions
  1. 50 31
      cmd/k8s-operator/e2e/setup.go
  2. 1 1
      flake.nix
  3. 0 1
      go.mod
  4. 1 1
      go.mod.sri
  5. 0 2
      go.sum
  6. 1 1
      shell.nix

+ 50 - 31
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 {

+ 1 - 1
flake.nix

@@ -151,4 +151,4 @@
     });
   };
 }
-# nix-direnv cache busting line: sha256-7Ak8bu6uQV+XmjzgW7yqFdptqocWYJS6grkCUAr1qlo=
+# nix-direnv cache busting line: sha256-kXdjsA1QIXS13vMBTMbxBJK4tewd6rVz0Csod+HtN10=

+ 0 - 1
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 (

+ 1 - 1
go.mod.sri

@@ -1 +1 @@
-sha256-7Ak8bu6uQV+XmjzgW7yqFdptqocWYJS6grkCUAr1qlo=
+sha256-kXdjsA1QIXS13vMBTMbxBJK4tewd6rVz0Csod+HtN10=

+ 0 - 2
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=

+ 1 - 1
shell.nix

@@ -16,4 +16,4 @@
 ) {
   src =  ./.;
 }).shellNix
-# nix-direnv cache busting line: sha256-7Ak8bu6uQV+XmjzgW7yqFdptqocWYJS6grkCUAr1qlo=
+# nix-direnv cache busting line: sha256-kXdjsA1QIXS13vMBTMbxBJK4tewd6rVz0Csod+HtN10=