golang.org/x/build@v0.0.0-20240506185731-218518f32b70/kubernetes/gke/gke_test.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gke_test
     6  
     7  import (
     8  	"context"
     9  	"flag"
    10  	"strings"
    11  	"testing"
    12  
    13  	"cloud.google.com/go/compute/metadata"
    14  	"golang.org/x/build/kubernetes"
    15  	"golang.org/x/build/kubernetes/gke"
    16  	"golang.org/x/oauth2"
    17  	"golang.org/x/oauth2/google"
    18  	compute "google.golang.org/api/compute/v1"
    19  	container "google.golang.org/api/container/v1"
    20  )
    21  
    22  // Note: The TestNewClient, TestDialPod, TestDialService, and TestGetNodes
    23  // tests require to be run on GCE and with Application Default Credentials
    24  // that have at least the container.clusters.list permission, at least one
    25  // GKE cluster, and possibly more.
    26  //
    27  // They're currently disabled on the Go builders; see golang.org/issue/28543.
    28  var flagRunGKETests = flag.Bool("run-gke-tests", false, "run gke tests that require special permissions")
    29  
    30  // Tests NewClient and also Dialer.
    31  func TestNewClient(t *testing.T) {
    32  	ctx := context.Background()
    33  	foreachCluster(t, func(cl *container.Cluster, kc *kubernetes.Client) {
    34  		_, err := kc.GetPods(ctx)
    35  		if err != nil {
    36  			t.Fatal(err)
    37  		}
    38  	})
    39  }
    40  
    41  func TestDialPod(t *testing.T) {
    42  	var passed bool
    43  	var candidates int
    44  	ctx := context.Background()
    45  	foreachCluster(t, func(cl *container.Cluster, kc *kubernetes.Client) {
    46  		if passed {
    47  			return
    48  		}
    49  		pods, err := kc.GetPods(ctx)
    50  		if err != nil {
    51  			t.Fatal(err)
    52  		}
    53  
    54  		for _, pod := range pods {
    55  			if pod.Status.Phase != "Running" {
    56  				continue
    57  			}
    58  			for _, container := range pod.Spec.Containers {
    59  				for _, port := range container.Ports {
    60  					if strings.ToLower(string(port.Protocol)) == "udp" || port.ContainerPort == 0 {
    61  						continue
    62  					}
    63  					candidates++
    64  					c, err := kc.DialPod(ctx, pod.Name, port.ContainerPort)
    65  					if err != nil {
    66  						t.Logf("Dial %q/%q/%d: %v", cl.Name, pod.Name, port.ContainerPort, err)
    67  						continue
    68  					}
    69  					c.Close()
    70  					t.Logf("Dialed %q/%q/%d.", cl.Name, pod.Name, port.ContainerPort)
    71  					passed = true
    72  					return
    73  				}
    74  			}
    75  		}
    76  	})
    77  	if candidates == 0 {
    78  		t.Skip("no pods to dial")
    79  	}
    80  	if !passed {
    81  		t.Errorf("dial failures")
    82  	}
    83  }
    84  
    85  func TestDialService(t *testing.T) {
    86  	var passed bool
    87  	var candidates int
    88  	ctx := context.Background()
    89  	foreachCluster(t, func(cl *container.Cluster, kc *kubernetes.Client) {
    90  		if passed {
    91  			return
    92  		}
    93  		svcs, err := kc.GetServices(ctx)
    94  		if err != nil {
    95  			t.Fatal(err)
    96  		}
    97  		for _, svc := range svcs {
    98  			eps, err := kc.GetServiceEndpoints(ctx, svc.Name, "")
    99  			if err != nil {
   100  				t.Fatal(err)
   101  			}
   102  			if len(eps) != 1 {
   103  				continue
   104  			}
   105  			candidates++
   106  			conn, err := kc.DialServicePort(ctx, svc.Name, "")
   107  			if err != nil {
   108  				t.Logf("%s: DialServicePort(%q) error: %v", cl.Name, svc.Name, err)
   109  				continue
   110  			}
   111  			conn.Close()
   112  			passed = true
   113  			t.Logf("Dialed cluster %q service %q.", cl.Name, svc.Name)
   114  			return
   115  		}
   116  
   117  	})
   118  	if candidates == 0 {
   119  		t.Skip("no services to dial")
   120  	}
   121  	if !passed {
   122  		t.Errorf("dial failures")
   123  	}
   124  }
   125  
   126  func foreachCluster(t *testing.T, fn func(*container.Cluster, *kubernetes.Client)) {
   127  	if testing.Short() {
   128  		t.Skip("skipping in short mode")
   129  	}
   130  	if !*flagRunGKETests {
   131  		t.Skip("-run-gke-tests not set")
   132  	}
   133  	ctx := context.Background()
   134  	ts, err := google.DefaultTokenSource(ctx, compute.CloudPlatformScope)
   135  	if err != nil {
   136  		t.Fatal(err)
   137  	}
   138  	httpClient := oauth2.NewClient(ctx, ts)
   139  	containerService, err := container.New(httpClient)
   140  	if err != nil {
   141  		t.Fatal(err)
   142  	}
   143  	proj, err := metadata.ProjectID()
   144  	if err != nil {
   145  		t.Fatal(err)
   146  	}
   147  	if _, err := ts.Token(); err != nil {
   148  		val, err := metadata.InstanceAttributeValue("service-accounts/default/token")
   149  		if val == "" {
   150  			t.Skip("skipping on GCE instance without a service account")
   151  		}
   152  		t.Skipf("default token source doesn't work; skipping test: %v", err)
   153  	}
   154  
   155  	clusters, err := containerService.Projects.Locations.Clusters.List("/project/" + proj + "/locations/-").Context(ctx).Do()
   156  	if err != nil {
   157  		t.Fatal(err)
   158  	}
   159  
   160  	if len(clusters.Clusters) == 0 {
   161  		t.Skip("no GKE clusters")
   162  	}
   163  	for _, cl := range clusters.Clusters {
   164  		kc, err := gke.NewClient(ctx, cl.Name, cl.Zone)
   165  		if err != nil {
   166  			t.Fatal(err)
   167  		}
   168  		fn(cl, kc)
   169  		kc.Close()
   170  	}
   171  }
   172  
   173  func TestGetNodes(t *testing.T) {
   174  	var passed bool
   175  	ctx := context.Background()
   176  	foreachCluster(t, func(cl *container.Cluster, kc *kubernetes.Client) {
   177  		if passed {
   178  			return
   179  		}
   180  		nodes, err := kc.GetNodes(ctx)
   181  		if err != nil {
   182  			t.Fatal(err)
   183  		}
   184  		t.Logf("%d nodes in cluster %s", len(nodes), cl.Name)
   185  		passed = true
   186  	})
   187  }