gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/tools/gvisor_k8s_tool/provider/gke/gke.go (about)

     1  // Copyright 2023 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package gke contains functions to interact with Google Kubernetes Engine.
    16  package gke
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"os"
    22  	"os/exec"
    23  	"path"
    24  	"strings"
    25  
    26  	"gvisor.dev/gvisor/tools/gvisor_k8s_tool/cluster"
    27  	"gvisor.dev/gvisor/tools/gvisor_k8s_tool/util"
    28  	"k8s.io/client-go/tools/clientcmd"
    29  )
    30  
    31  // ClusterURL represents a GKE cluster URL of the format:
    32  // "projects/$MYPROJECT/locations/$CONTINENT-$LOCATION/clusters/$CLUSTER"
    33  type ClusterURL struct {
    34  	ProjectID   string
    35  	Location    string
    36  	ClusterName string
    37  }
    38  
    39  // String returns the cluster URL string.
    40  func (c ClusterURL) String() string {
    41  	return fmt.Sprintf("projects/%s/locations/%s/clusters/%s", c.ProjectID, c.Location, c.ClusterName)
    42  }
    43  
    44  // NewClusterURL parses the cluster URL.
    45  func NewClusterURL(url string) (ClusterURL, error) {
    46  	if !strings.HasPrefix(url, "projects/") {
    47  		return ClusterURL{}, fmt.Errorf("invalid GKE cluster URL (expecting 'projects/MYPROJECT/locations/LOCATION/clusters/MYCLUSTER'): %q", url)
    48  	}
    49  	parts := strings.Split(url, "/")
    50  	if len(parts) != 6 {
    51  		return ClusterURL{}, fmt.Errorf("invalid GKE cluster URL (expecting 6 slash-delimited parts, got %d): %q", len(parts), url)
    52  	}
    53  	if parts[0] != "projects" || parts[2] != "locations" || parts[4] != "clusters" {
    54  		return ClusterURL{}, fmt.Errorf("invalid GKE cluster URL (expecting 'projects/MYPROJECT/locations/LOCATION/clusters/MYCLUSTER'): %q", url)
    55  	}
    56  	return ClusterURL{
    57  		ProjectID:   parts[1],
    58  		Location:    parts[3],
    59  		ClusterName: parts[5],
    60  	}, nil
    61  }
    62  
    63  // GetCluster returns a Kubernetes client for the given named cluster.
    64  func GetCluster(ctx context.Context, clusterURL ClusterURL) (*cluster.Cluster, error) {
    65  	tmpDir, cleanTmp, err := util.TempDir()
    66  	defer cleanTmp()
    67  	if err != nil {
    68  		return nil, fmt.Errorf("failed to create temp dir: %w", err)
    69  	}
    70  	credFilePath := path.Join(tmpDir, fmt.Sprintf("%s.credential", clusterURL.ClusterName))
    71  	f, err := os.Create(credFilePath)
    72  	if err != nil {
    73  		return nil, fmt.Errorf("failed to create cred file: %v", err)
    74  	}
    75  	f.Close()
    76  	cmd := exec.CommandContext(ctx, "gcloud", "--project", clusterURL.ProjectID, "container", "clusters", "get-credentials", clusterURL.ClusterName, "--location", clusterURL.Location)
    77  	cmd.Env = append(cmd.Environ(), fmt.Sprintf("KUBECONFIG=%s", credFilePath))
    78  	out, err := cmd.CombinedOutput()
    79  	if err != nil {
    80  		return nil, fmt.Errorf("failed to set credentials: %v; output: %s", err, string(out))
    81  	}
    82  	configBytes, err := os.ReadFile(credFilePath)
    83  	if err != nil {
    84  		return nil, fmt.Errorf("failed to read kubectl config file: %w", err)
    85  	}
    86  	kubeCfg, err := clientcmd.RESTConfigFromKubeConfig(configBytes)
    87  	if err != nil {
    88  		return nil, fmt.Errorf("failed to parse kubectl config file: %w", err)
    89  	}
    90  	gkeCluster, err := cluster.New(kubeCfg)
    91  	if err != nil {
    92  		return nil, fmt.Errorf("failed to instantiate GKE cluster client: %w", err)
    93  	}
    94  	return gkeCluster, nil
    95  }