github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/cmd/install.go (about)

     1  // Copyright 2019 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 cmd
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"log"
    23  	"os"
    24  	"path"
    25  
    26  	"github.com/google/subcommands"
    27  	"github.com/SagerNet/gvisor/runsc/flag"
    28  )
    29  
    30  // Install implements subcommands.Command.
    31  type Install struct {
    32  	ConfigFile   string
    33  	Runtime      string
    34  	Experimental bool
    35  }
    36  
    37  // Name implements subcommands.Command.Name.
    38  func (*Install) Name() string {
    39  	return "install"
    40  }
    41  
    42  // Synopsis implements subcommands.Command.Synopsis.
    43  func (*Install) Synopsis() string {
    44  	return "adds a runtime to docker daemon configuration"
    45  }
    46  
    47  // Usage implements subcommands.Command.Usage.
    48  func (*Install) Usage() string {
    49  	return `install [flags] <name> [-- [args...]] -- if provided, args are passed to the runtime
    50  `
    51  }
    52  
    53  // SetFlags implements subcommands.Command.SetFlags.
    54  func (i *Install) SetFlags(fs *flag.FlagSet) {
    55  	fs.StringVar(&i.ConfigFile, "config_file", "/etc/docker/daemon.json", "path to Docker daemon config file")
    56  	fs.StringVar(&i.Runtime, "runtime", "runsc", "runtime name")
    57  	fs.BoolVar(&i.Experimental, "experimental", false, "enable experimental features")
    58  }
    59  
    60  // Execute implements subcommands.Command.Execute.
    61  func (i *Install) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
    62  	// Grab the name and arguments.
    63  	runtimeArgs := f.Args()
    64  
    65  	// Extract the executable.
    66  	path, err := os.Executable()
    67  	if err != nil {
    68  		log.Fatalf("Error reading current exectuable: %v", err)
    69  	}
    70  
    71  	// Load the configuration file.
    72  	c, err := readConfig(i.ConfigFile)
    73  	if err != nil {
    74  		log.Fatalf("Error reading config file %q: %v", i.ConfigFile, err)
    75  	}
    76  
    77  	// Add the given runtime.
    78  	var rts map[string]interface{}
    79  	if i, ok := c["runtimes"]; ok {
    80  		rts = i.(map[string]interface{})
    81  	} else {
    82  		rts = make(map[string]interface{})
    83  		c["runtimes"] = rts
    84  	}
    85  	rts[i.Runtime] = struct {
    86  		Path        string   `json:"path,omitempty"`
    87  		RuntimeArgs []string `json:"runtimeArgs,omitempty"`
    88  	}{
    89  		Path:        path,
    90  		RuntimeArgs: runtimeArgs,
    91  	}
    92  
    93  	// Set experimental if required.
    94  	if i.Experimental {
    95  		c["experimental"] = true
    96  	}
    97  
    98  	// Write out the runtime.
    99  	if err := writeConfig(c, i.ConfigFile); err != nil {
   100  		log.Fatalf("Error writing config file %q: %v", i.ConfigFile, err)
   101  	}
   102  
   103  	// Success.
   104  	log.Printf("Added runtime %q with arguments %v to %q.", i.Runtime, runtimeArgs, i.ConfigFile)
   105  	return subcommands.ExitSuccess
   106  }
   107  
   108  // Uninstall implements subcommands.Command.
   109  type Uninstall struct {
   110  	ConfigFile string
   111  	Runtime    string
   112  }
   113  
   114  // Name implements subcommands.Command.Name.
   115  func (*Uninstall) Name() string {
   116  	return "uninstall"
   117  }
   118  
   119  // Synopsis implements subcommands.Command.Synopsis.
   120  func (*Uninstall) Synopsis() string {
   121  	return "removes a runtime from docker daemon configuration"
   122  }
   123  
   124  // Usage implements subcommands.Command.Usage.
   125  func (*Uninstall) Usage() string {
   126  	return `uninstall [flags] <name>
   127  `
   128  }
   129  
   130  // SetFlags implements subcommands.Command.SetFlags.
   131  func (u *Uninstall) SetFlags(fs *flag.FlagSet) {
   132  	fs.StringVar(&u.ConfigFile, "config_file", "/etc/docker/daemon.json", "path to Docker daemon config file")
   133  	fs.StringVar(&u.Runtime, "runtime", "runsc", "runtime name")
   134  }
   135  
   136  // Execute implements subcommands.Command.Execute.
   137  func (u *Uninstall) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
   138  	log.Printf("Removing runtime %q from %q.", u.Runtime, u.ConfigFile)
   139  
   140  	c, err := readConfig(u.ConfigFile)
   141  	if err != nil {
   142  		log.Fatalf("Error reading config file %q: %v", u.ConfigFile, err)
   143  	}
   144  
   145  	var rts map[string]interface{}
   146  	if i, ok := c["runtimes"]; ok {
   147  		rts = i.(map[string]interface{})
   148  	} else {
   149  		log.Fatalf("runtime %q not found", u.Runtime)
   150  	}
   151  	if _, ok := rts[u.Runtime]; !ok {
   152  		log.Fatalf("runtime %q not found", u.Runtime)
   153  	}
   154  	delete(rts, u.Runtime)
   155  
   156  	if err := writeConfig(c, u.ConfigFile); err != nil {
   157  		log.Fatalf("Error writing config file %q: %v", u.ConfigFile, err)
   158  	}
   159  	return subcommands.ExitSuccess
   160  }
   161  
   162  func readConfig(path string) (map[string]interface{}, error) {
   163  	// Read the configuration data.
   164  	configBytes, err := ioutil.ReadFile(path)
   165  	if err != nil && !os.IsNotExist(err) {
   166  		return nil, err
   167  	}
   168  
   169  	// Unmarshal the configuration.
   170  	c := make(map[string]interface{})
   171  	if len(configBytes) > 0 {
   172  		if err := json.Unmarshal(configBytes, &c); err != nil {
   173  			return nil, err
   174  		}
   175  	}
   176  
   177  	return c, nil
   178  }
   179  
   180  func writeConfig(c map[string]interface{}, filename string) error {
   181  	// Marshal the configuration.
   182  	b, err := json.MarshalIndent(c, "", "    ")
   183  	if err != nil {
   184  		return err
   185  	}
   186  
   187  	// Copy the old configuration.
   188  	old, err := ioutil.ReadFile(filename)
   189  	if err != nil {
   190  		if !os.IsNotExist(err) {
   191  			return fmt.Errorf("error reading config file %q: %v", filename, err)
   192  		}
   193  	} else {
   194  		if err := ioutil.WriteFile(filename+"~", old, 0644); err != nil {
   195  			return fmt.Errorf("error backing up config file %q: %v", filename, err)
   196  		}
   197  	}
   198  
   199  	// Make the necessary directories.
   200  	if err := os.MkdirAll(path.Dir(filename), 0755); err != nil {
   201  		return fmt.Errorf("error creating config directory for %q: %v", filename, err)
   202  	}
   203  
   204  	// Write the new configuration.
   205  	if err := ioutil.WriteFile(filename, b, 0644); err != nil {
   206  		return fmt.Errorf("error writing config file %q: %v", filename, err)
   207  	}
   208  
   209  	return nil
   210  }