golang.org/x/build@v0.0.0-20240506185731-218518f32b70/kubernetes/gke/gke.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 contains code for interacting with Google Container Engine (GKE),
     6  // the hosted version of Kubernetes on Google Cloud Platform.
     7  //
     8  // The API is not subject to the Go 1 compatibility promise and may change at
     9  // any time. Users should vendor this package and deal with API changes.
    10  package gke
    11  
    12  import (
    13  	"context"
    14  	"crypto/tls"
    15  	"crypto/x509"
    16  	"encoding/base64"
    17  	"fmt"
    18  	"net/http"
    19  
    20  	"cloud.google.com/go/compute/metadata"
    21  
    22  	"golang.org/x/build/kubernetes"
    23  	"golang.org/x/oauth2"
    24  	"golang.org/x/oauth2/google"
    25  	compute "google.golang.org/api/compute/v1"
    26  	"google.golang.org/api/container/v1"
    27  )
    28  
    29  // ClientOpt represents an option that can be passed to the Client function.
    30  type ClientOpt interface {
    31  	modify(*clientOpt)
    32  }
    33  
    34  type clientOpt struct {
    35  	Project     string
    36  	TokenSource oauth2.TokenSource
    37  	Namespace   string
    38  }
    39  
    40  type clientOptFunc func(*clientOpt)
    41  
    42  func (f clientOptFunc) modify(o *clientOpt) { f(o) }
    43  
    44  // OptProject returns an option setting the GCE Project ID to projectName.
    45  // This is the named project ID, not the numeric ID.
    46  // If unspecified, the current active project ID is used, if the program is running
    47  // on a GCE instance.
    48  func OptProject(projectName string) ClientOpt {
    49  	return clientOptFunc(func(o *clientOpt) {
    50  		o.Project = projectName
    51  	})
    52  }
    53  
    54  // OptTokenSource sets the oauth2 token source for making
    55  // authenticated requests to the GKE API. If unset, the default token
    56  // source is used (https://godoc.org/golang.org/x/oauth2/google#DefaultTokenSource).
    57  func OptTokenSource(ts oauth2.TokenSource) ClientOpt {
    58  	return clientOptFunc(func(o *clientOpt) {
    59  		o.TokenSource = ts
    60  	})
    61  }
    62  
    63  // OptNamespace sets the Kubernetes namespace to look in.
    64  func OptNamespace(namespace string) ClientOpt {
    65  	return clientOptFunc(func(o *clientOpt) {
    66  		o.Namespace = namespace
    67  	})
    68  }
    69  
    70  // NewClient returns an Kubernetes client to a GKE cluster.
    71  func NewClient(ctx context.Context, clusterName string, location string, opts ...ClientOpt) (*kubernetes.Client, error) {
    72  	opt := clientOpt{Namespace: "default"}
    73  	for _, o := range opts {
    74  		o.modify(&opt)
    75  	}
    76  	if opt.TokenSource == nil {
    77  		var err error
    78  		opt.TokenSource, err = google.DefaultTokenSource(ctx, compute.CloudPlatformScope)
    79  		if err != nil {
    80  			return nil, fmt.Errorf("failed to get a token source: %v", err)
    81  		}
    82  	}
    83  	if opt.Project == "" {
    84  		proj, err := metadata.ProjectID()
    85  		if err != nil {
    86  			return nil, fmt.Errorf("metadata.ProjectID: %v", err)
    87  		}
    88  		opt.Project = proj
    89  	}
    90  
    91  	httpClient := oauth2.NewClient(ctx, opt.TokenSource)
    92  	containerService, err := container.New(httpClient)
    93  	if err != nil {
    94  		return nil, fmt.Errorf("could not create client for Google Container Engine: %v", err)
    95  	}
    96  
    97  	cluster, err := containerService.Projects.Locations.Clusters.Get(fmt.Sprintf("projects/%s/locations/%s/clusters/%s", opt.Project, location, clusterName)).Context(ctx).Do()
    98  	if err != nil {
    99  		return nil, fmt.Errorf("cluster %q could not be found in project %q, location %q: %v", clusterName, opt.Project, location, err)
   100  	}
   101  
   102  	// Connect to Kubernetes using OAuth authentication, trusting its CA.
   103  	caPool := x509.NewCertPool()
   104  	caCertPEM, err := base64.StdEncoding.DecodeString(cluster.MasterAuth.ClusterCaCertificate)
   105  	if err != nil {
   106  		return nil, fmt.Errorf("invalid base64 in ClusterCaCertificate: %v", err)
   107  	}
   108  	caPool.AppendCertsFromPEM(caCertPEM)
   109  	kubeHTTPClient := &http.Client{
   110  		Transport: &oauth2.Transport{
   111  			Source: opt.TokenSource,
   112  			Base: &http.Transport{
   113  				TLSClientConfig: &tls.Config{
   114  					RootCAs: caPool,
   115  				},
   116  			},
   117  		},
   118  	}
   119  	kubeClient, err := kubernetes.NewClient("https://"+cluster.Endpoint, opt.Namespace, kubeHTTPClient)
   120  	if err != nil {
   121  		return nil, fmt.Errorf("kubernetes HTTP client could not be created: %v", err)
   122  	}
   123  	return kubeClient, nil
   124  }