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 }