github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/rkt/rkt.go (about)

     1  // Copyright 2014 The rkt 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 main
    16  
    17  import (
    18  	"fmt"
    19  	"io"
    20  	"os"
    21  	"path/filepath"
    22  	"runtime/pprof"
    23  	"text/tabwriter"
    24  
    25  	"github.com/rkt/rkt/common"
    26  	"github.com/rkt/rkt/pkg/keystore"
    27  	"github.com/rkt/rkt/pkg/log"
    28  	"github.com/rkt/rkt/rkt/config"
    29  	rktflag "github.com/rkt/rkt/rkt/flag"
    30  	"github.com/spf13/cobra"
    31  )
    32  
    33  const (
    34  	cliName        = "rkt"
    35  	cliDescription = "rkt, the application container runner"
    36  
    37  	defaultDataDir = "/var/lib/rkt"
    38  )
    39  
    40  type absDir string
    41  
    42  func (d *absDir) String() string {
    43  	return (string)(*d)
    44  }
    45  
    46  func (d *absDir) Set(str string) error {
    47  	if str == "" {
    48  		return fmt.Errorf(`"" is not a valid directory`)
    49  	}
    50  
    51  	dir, err := filepath.Abs(str)
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	*d = (absDir)(dir)
    57  
    58  	return nil
    59  }
    60  
    61  func (d *absDir) Type() string {
    62  	return "absolute-directory"
    63  }
    64  
    65  var (
    66  	tabOut      *tabwriter.Writer
    67  	globalFlags = struct {
    68  		Dir                string
    69  		SystemConfigDir    string
    70  		LocalConfigDir     string
    71  		UserConfigDir      string
    72  		Debug              bool
    73  		Help               bool
    74  		InsecureFlags      *rktflag.SecFlags
    75  		TrustKeysFromHTTPS bool
    76  
    77  		// Hidden flags for profiling.
    78  		CPUProfile string
    79  		MemProfile string
    80  	}{
    81  		Dir:             defaultDataDir,
    82  		SystemConfigDir: common.DefaultSystemConfigDir,
    83  		LocalConfigDir:  common.DefaultLocalConfigDir,
    84  	}
    85  
    86  	cachedConfig  *config.Config
    87  	cachedDataDir string
    88  	cmdExitCode   int
    89  
    90  	stderr *log.Logger
    91  	stdout *log.Logger
    92  )
    93  
    94  var cmdRkt = &cobra.Command{
    95  	Use:   "rkt [command]",
    96  	Short: cliDescription,
    97  	Long: `A CLI for running app containers on Linux.
    98  
    99  To get the help on any specific command, run "rkt help command".`,
   100  	BashCompletionFunction: bashCompletionFunc,
   101  	Run: runMissingCommand,
   102  }
   103  
   104  func init() {
   105  	sf, err := rktflag.NewSecFlags("none")
   106  	if err != nil {
   107  		fmt.Fprintf(os.Stderr, "rkt: problem initializing: %v", err)
   108  		os.Exit(254)
   109  	}
   110  
   111  	globalFlags.InsecureFlags = sf
   112  
   113  	cmdRkt.PersistentFlags().BoolVar(&globalFlags.Debug, "debug", false, "print out more debug information to stderr")
   114  	cmdRkt.PersistentFlags().Var((*absDir)(&globalFlags.Dir), "dir", "rkt data directory")
   115  	cmdRkt.PersistentFlags().Var((*absDir)(&globalFlags.SystemConfigDir), "system-config", "system configuration directory")
   116  	cmdRkt.PersistentFlags().Var((*absDir)(&globalFlags.LocalConfigDir), "local-config", "local configuration directory")
   117  	cmdRkt.PersistentFlags().Var((*absDir)(&globalFlags.UserConfigDir), "user-config", "user configuration directory")
   118  	cmdRkt.PersistentFlags().Var(globalFlags.InsecureFlags, "insecure-options",
   119  		fmt.Sprintf("comma-separated list of security features to disable. Allowed values: %s",
   120  			globalFlags.InsecureFlags.PermissibleString()))
   121  	cmdRkt.PersistentFlags().BoolVar(&globalFlags.TrustKeysFromHTTPS, "trust-keys-from-https",
   122  		false, "automatically trust gpg keys fetched from https")
   123  	cmdRkt.PersistentFlags().StringVar(&globalFlags.CPUProfile, "cpuprofile", "", "write CPU profile to the file")
   124  	cmdRkt.PersistentFlags().MarkHidden("cpuprofile")
   125  	cmdRkt.PersistentFlags().StringVar(&globalFlags.MemProfile, "memprofile", "", "write memory profile to the file")
   126  	cmdRkt.PersistentFlags().MarkHidden("memprofile")
   127  
   128  	// Run this before the execution of each subcommand to set up output
   129  	cmdRkt.PersistentPreRun = func(cmd *cobra.Command, args []string) {
   130  		stderr = log.New(os.Stderr, cmd.Name(), globalFlags.Debug)
   131  		stdout = log.New(os.Stdout, "", false)
   132  	}
   133  
   134  	cobra.EnablePrefixMatching = true
   135  }
   136  
   137  func getTabOutWithWriter(writer io.Writer) *tabwriter.Writer {
   138  	aTabOut := new(tabwriter.Writer)
   139  
   140  	aTabOut.Init(writer, 0, 8, 1, '\t', 0)
   141  
   142  	return aTabOut
   143  }
   144  
   145  func startProfile() (cpufile *os.File, memfile *os.File, err error) {
   146  	if globalFlags.CPUProfile != "" {
   147  		cpufile, err = os.Create(globalFlags.CPUProfile)
   148  		if err != nil {
   149  			return nil, nil, fmt.Errorf("cannot create cpu profile file %q: %v", globalFlags.CPUProfile, err)
   150  		}
   151  		pprof.StartCPUProfile(cpufile)
   152  	}
   153  	if globalFlags.MemProfile != "" {
   154  		memfile, err = os.Create(globalFlags.MemProfile)
   155  		if err != nil {
   156  			return nil, nil, fmt.Errorf("cannot create memory profile file %q: %v", globalFlags.MemProfile, err)
   157  		}
   158  	}
   159  	return cpufile, memfile, nil
   160  }
   161  
   162  func stopProfile(cpuprofile, memprofile *os.File) {
   163  	if globalFlags.CPUProfile != "" {
   164  		pprof.StopCPUProfile()
   165  		cpuprofile.Close()
   166  	}
   167  	if globalFlags.MemProfile != "" {
   168  		pprof.WriteHeapProfile(memprofile)
   169  		memprofile.Close()
   170  	}
   171  }
   172  
   173  // runWrapper returns a func(cmd *cobra.Command, args []string) that internally
   174  // will add command function return code and the reinsertion of the "--" flag
   175  // terminator.
   176  func runWrapper(cf func(cmd *cobra.Command, args []string) (exit int)) func(cmd *cobra.Command, args []string) {
   177  	return func(cmd *cobra.Command, args []string) {
   178  		cpufile, memfile, err := startProfile()
   179  		if err != nil {
   180  			stderr.PrintE("cannot setup profiling", err)
   181  			cmdExitCode = 254
   182  			return
   183  		}
   184  		defer stopProfile(cpufile, memfile)
   185  
   186  		cmdExitCode = cf(cmd, args)
   187  	}
   188  }
   189  
   190  // ensureSuperuser will error out if the effective UID of the current process
   191  // is not zero. Otherwise, it will invoke the supplied cobra command.
   192  func ensureSuperuser(cf func(cmd *cobra.Command, args []string)) func(cmd *cobra.Command, args []string) {
   193  	return func(cmd *cobra.Command, args []string) {
   194  		if os.Geteuid() != 0 {
   195  			stderr.Print("cannot run as unprivileged user")
   196  			cmdExitCode = 254
   197  			return
   198  		}
   199  
   200  		cf(cmd, args)
   201  	}
   202  }
   203  
   204  func runMissingCommand(cmd *cobra.Command, args []string) {
   205  	stderr.Print("missing command")
   206  	cmd.HelpFunc()(cmd, args)
   207  	cmdExitCode = 2 // invalid argument
   208  }
   209  
   210  // where pod directories are created and locked before moving to prepared
   211  func embryoDir() string {
   212  	return filepath.Join(getDataDir(), "pods", "embryo")
   213  }
   214  
   215  // where pod trees reside during (locked) and after failing to complete preparation (unlocked)
   216  func prepareDir() string {
   217  	return filepath.Join(getDataDir(), "pods", "prepare")
   218  }
   219  
   220  // where pod trees reside upon successful preparation
   221  func preparedDir() string {
   222  	return filepath.Join(getDataDir(), "pods", "prepared")
   223  }
   224  
   225  // where pod trees reside once run
   226  func runDir() string {
   227  	return filepath.Join(getDataDir(), "pods", "run")
   228  }
   229  
   230  // where pod trees reside once exited & marked as garbage by a gc pass
   231  func exitedGarbageDir() string {
   232  	return filepath.Join(getDataDir(), "pods", "exited-garbage")
   233  }
   234  
   235  // where never-executed pod trees reside once marked as garbage by a gc pass (failed prepares, expired prepareds)
   236  func garbageDir() string {
   237  	return filepath.Join(getDataDir(), "pods", "garbage")
   238  }
   239  
   240  func storeDir() string {
   241  	return filepath.Join(getDataDir(), "cas")
   242  }
   243  
   244  // TODO(sgotti) backward compatibility with the current tree store paths. Needs a migration path to better paths.
   245  func treeStoreDir() string {
   246  	return filepath.Join(getDataDir(), "cas")
   247  }
   248  
   249  func getKeystore() *keystore.Keystore {
   250  	if globalFlags.InsecureFlags.SkipImageCheck() {
   251  		return nil
   252  	}
   253  	config := keystore.NewConfig(globalFlags.SystemConfigDir, globalFlags.LocalConfigDir)
   254  	return keystore.New(config)
   255  }
   256  
   257  func getDataDir() string {
   258  	if cachedDataDir == "" {
   259  		cachedDataDir = calculateDataDir()
   260  	}
   261  
   262  	return cachedDataDir
   263  }
   264  
   265  func calculateDataDir() string {
   266  	var dataDir string
   267  
   268  	// If --dir parameter is passed, then use this value.
   269  	dirFlag := cmdRkt.PersistentFlags().Lookup("dir")
   270  	if dirFlag == nil {
   271  		// should not happen
   272  		panic(`"--dir" flag not found`)
   273  	}
   274  
   275  	if dirFlag.Changed {
   276  		dataDir = globalFlags.Dir
   277  	}
   278  
   279  	// If above fails, then try to get the value from configuration.
   280  	if dataDir == "" {
   281  		config, err := getConfig()
   282  		if err != nil {
   283  			stderr.PrintE("cannot get configuration", err)
   284  			os.Exit(254)
   285  		}
   286  
   287  		if config.Paths.DataDir != "" {
   288  			dataDir = config.Paths.DataDir
   289  		} else {
   290  			dataDir = defaultDataDir
   291  		}
   292  	}
   293  
   294  	// Resolve symlinks
   295  	realDataDir, err := filepath.EvalSymlinks(dataDir)
   296  	if err != nil {
   297  		if os.IsNotExist(err) {
   298  			realDataDir = dataDir
   299  		} else {
   300  			stderr.PrintE(fmt.Sprintf("cannot evaluate dataDir %q real path", dataDir), err)
   301  			os.Exit(254)
   302  		}
   303  	}
   304  
   305  	// If above fails, then use the default.
   306  	return realDataDir
   307  }
   308  
   309  func getConfig() (*config.Config, error) {
   310  	if cachedConfig != nil {
   311  		return cachedConfig, nil
   312  	}
   313  
   314  	dirs := []string{
   315  		globalFlags.SystemConfigDir,
   316  		globalFlags.LocalConfigDir,
   317  	}
   318  
   319  	if globalFlags.UserConfigDir != "" {
   320  		dirs = append(dirs, globalFlags.UserConfigDir)
   321  	}
   322  
   323  	cfg, err := config.GetConfigFrom(dirs...)
   324  	if err != nil {
   325  		return nil, err
   326  	}
   327  
   328  	cachedConfig = cfg
   329  
   330  	return cfg, nil
   331  }
   332  
   333  func lockDir() string {
   334  	return filepath.Join(getDataDir(), "locks")
   335  }