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 }