gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/tools/gvisor_k8s_tool/cmd/install/install.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 install provides a function to install gVisor in a k8s cluster.
    16  package install
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"strings"
    22  
    23  	"github.com/google/subcommands"
    24  	"gvisor.dev/gvisor/pkg/log"
    25  	"gvisor.dev/gvisor/runsc/cmd/util"
    26  	"gvisor.dev/gvisor/runsc/flag"
    27  	"gvisor.dev/gvisor/tools/gvisor_k8s_tool/cluster"
    28  	"gvisor.dev/gvisor/tools/gvisor_k8s_tool/provider/clusterflag"
    29  	"gvisor.dev/gvisor/tools/gvisor_k8s_tool/spec"
    30  )
    31  
    32  // Install installs runsc from the given image in the given cluster.
    33  func Install(ctx context.Context, c *cluster.Cluster, image string, options spec.InstallOptions) error {
    34  	ds := spec.RunscInstallDaemonSet(image, options)
    35  	// Delete a daemonset of the same name in the same namespace in case there is a collision.
    36  	if err := c.DeleteDaemonset(ctx, ds); err != nil && !strings.Contains(err.Error(), "not found") {
    37  		return fmt.Errorf("failed to delete DaemonSet %q in namespace %q: %w", ds.Name, ds.Namespace, err)
    38  	}
    39  
    40  	// Create the daemonset, but don't delete it so that we can get logs if there
    41  	// is a problem.
    42  	log.Infof("Creating DaemonSet %q in namespace %q...", ds.Name, ds.Namespace)
    43  	ds, err := c.CreateDaemonset(ctx, ds)
    44  	if err != nil {
    45  		return fmt.Errorf("failed to create DaemonSet %q in namespace %q: %w", ds.Name, ds.Namespace, err)
    46  	}
    47  
    48  	log.Infof("Waiting for DaemonSet %q in namespace %q...", ds.Name, ds.Namespace)
    49  	if err := c.WaitForDaemonset(ctx, ds); err != nil {
    50  		return fmt.Errorf("failed to wait for daemonset: %v", err)
    51  	}
    52  
    53  	log.Infof("DaemonSet %q in namespace %q complete.", ds.Name, ds.Namespace)
    54  	return nil
    55  }
    56  
    57  // Command implements subcommands.Command.
    58  type Command struct {
    59  	Image               string
    60  	Cluster             clusterflag.Flag
    61  	DaemonSetName       string
    62  	DaemonSetNamespace  string
    63  	PauseContainerImage string
    64  }
    65  
    66  // Name implements subcommands.Command.Name.
    67  func (*Command) Name() string {
    68  	return "install"
    69  }
    70  
    71  // Synopsis implements subcommands.Command.Synopsis.
    72  func (*Command) Synopsis() string {
    73  	return "install gVisor in a kubernetes cluster"
    74  }
    75  
    76  // Usage implements subcommands.Command.Usage.
    77  func (*Command) Usage() string {
    78  	return `install --image=<image> --cluster=<cluster_info>
    79  
    80  Where "<image>" is the name of the runsc installer image,
    81  and <cluster_info> contains information on how to connect
    82  to the Kubernetes cluster to install to.
    83  
    84  <cluster_info> can take the form of:
    85    * --cluster=kube:<context_name>
    86        ... where "<context_name>" is the name of a context in the
    87        kubectl config file at $KUBECONFIG.
    88        If $KUBECONFIG is not defined, it defaults to
    89        $HOME/.kube/config.
    90        If the context_name is empty, the default ("current")
    91        context in the config file is used.
    92    * --cluster=gke:projects/<project>/locations/<location>/clusters/<cluster>
    93        ... where <project>, <location> and <cluster> identify the project,
    94        location, and name of the Google Kubernetes Engine cluster.
    95  
    96  `
    97  }
    98  
    99  // SetFlags implements subcommands.Command.SetFlags.
   100  func (c *Command) SetFlags(f *flag.FlagSet) {
   101  	f.StringVar(&c.Image, "image", "", "runsc installer image")
   102  	f.Var(&c.Cluster, "cluster", "Kubernetes cluster to install runsc into")
   103  	f.StringVar(&c.DaemonSetName, "daemonset-name", "gvisor-runsc-installer", "name of the runsc installer DaemonSet; any previously-existing DaemonSet under this name will be deleted")
   104  	f.StringVar(&c.DaemonSetNamespace, "daemonset-namespace", spec.SystemNamespace, "namespace of the runsc installer DaemonSet")
   105  	f.StringVar(&c.PauseContainerImage, "pause-container-image", spec.PauseContainerImage, "container image that does nothing, used as placeholder in the DaemonSet")
   106  }
   107  
   108  // Execute implements subcommands.Command.Execute.
   109  // It installs gVisor in a Kubernetes cluster.
   110  func (c *Command) Execute(ctx context.Context, f *flag.FlagSet, _ ...any) subcommands.ExitStatus {
   111  	if err := c.Cluster.Valid(); err != nil {
   112  		f.Usage()
   113  		return subcommands.ExitUsageError
   114  	}
   115  	clusterClient, err := c.Cluster.Cluster(ctx)
   116  	if err != nil {
   117  		util.Fatalf("Cannot initialize cluster client: %v", err)
   118  	}
   119  	var labels map[string]string
   120  	var nodeSelector map[string]string
   121  	switch c.Cluster.Provider {
   122  	case clusterflag.GKE:
   123  		labels = spec.GKESandboxNodeSelector
   124  		nodeSelector = spec.GKESandboxNodeSelector
   125  	default:
   126  	}
   127  	if err := Install(ctx, clusterClient, c.Image, spec.InstallOptions{
   128  		DaemonSetName:       c.DaemonSetName,
   129  		DaemonSetNamespace:  c.DaemonSetNamespace,
   130  		PauseContainerImage: c.PauseContainerImage,
   131  		Labels:              labels,
   132  		NodeSelector:        nodeSelector,
   133  	}); err != nil {
   134  		util.Fatalf("Install failed: %v", err)
   135  	}
   136  	return subcommands.ExitSuccess
   137  }