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 }