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