github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/rkt/status.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 //+build linux 16 17 package main 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "strconv" 23 "time" 24 25 lib "github.com/rkt/rkt/lib" 26 pkgPod "github.com/rkt/rkt/pkg/pod" 27 "github.com/spf13/cobra" 28 "golang.org/x/net/context" 29 ) 30 31 var ( 32 cmdStatus = &cobra.Command{ 33 Use: "status [--wait=bool|timeout] [--wait-ready=bool|timeout] --uuid-file=FILE | UUID", 34 Short: "Check the status of a rkt pod", 35 Long: `Prints assorted information about the pod such as its state, pid and exit status. 36 37 The --wait and --wait-ready flags accept boolean or timeout values. If set to true, wait indefinitely. If set to false, don't wait at all. 38 They can also be set to a duration. If the duration is less than zero, wait indefinitely. If the duration is zero, don't wait at all.`, 39 Run: runWrapper(runStatus), 40 } 41 flagWait string 42 flagWaitReady string 43 ) 44 45 const ( 46 regularStatusDir = "stage1/rootfs/rkt/status" 47 cmdStatusName = "status" 48 ) 49 50 func init() { 51 cmdRkt.AddCommand(cmdStatus) 52 cmdStatus.Flags().StringVar(&flagWait, "wait", "false", `toggles waiting for the pod to finish. Use the output to determine the actual terminal state.`) 53 cmdStatus.Flags().StringVar(&flagWaitReady, "wait-ready", "false", `toggles waiting until the pod is ready.`) 54 cmdStatus.Flags().Var(&flagFormat, "format", `choose the output format. Allowed format includes 'json', 'json-pretty'. If empty, then the result is printed as key value pairs`) 55 56 cmdStatus.Flags().Lookup("wait").NoOptDefVal = "true" 57 cmdStatus.Flags().Lookup("wait-ready").NoOptDefVal = "true" 58 cmdStatus.Flags().StringVar(&flagUUIDFile, "uuid-file", "", "read pod UUID from file instead of argument") 59 } 60 61 func runStatus(cmd *cobra.Command, args []string) (exit int) { 62 var podUUID string 63 64 switch { 65 case len(args) == 0 && flagUUIDFile != "": 66 UUID, err := pkgPod.ReadUUIDFromFile(flagUUIDFile) 67 if err != nil { 68 stderr.PrintE("unable to resolve UUID from file", err) 69 return 1 70 } 71 podUUID = UUID 72 73 case len(args) == 1 && flagUUIDFile == "": 74 podUUID = args[0] 75 76 default: 77 cmd.Usage() 78 return 254 79 } 80 81 dWait, err := parseDuration(flagWait) 82 if err != nil { 83 cmd.Usage() 84 return 254 85 } 86 87 dReady, err := parseDuration(flagWaitReady) 88 if err != nil { 89 cmd.Usage() 90 return 254 91 } 92 p, err := pkgPod.PodFromUUIDString(getDataDir(), podUUID) 93 if err != nil { 94 stderr.PrintE("problem retrieving pod", err) 95 return 254 96 } 97 defer p.Close() 98 99 if dReady != 0 { 100 if err := p.WaitReady(newContext(dReady)); err != nil { 101 stderr.PrintE("error waiting for pod readiness", err) 102 return 254 103 } 104 } 105 106 if dWait != 0 { 107 if err := p.WaitFinished(newContext(dWait)); err != nil { 108 stderr.PrintE("error waiting for pod to finish", err) 109 return 254 110 } 111 } 112 113 if err = printStatus(p); err != nil { 114 stderr.PrintE("unable to print status", err) 115 return 254 116 } 117 118 return 0 119 } 120 121 // parseDuration converts the given string s to a duration value. 122 // If it is empty string or a true boolean value according to strconv.ParseBool, a negative duration is returned. 123 // If the boolean value is false, a 0 duration is returned. 124 // If the string s is a duration value, then it is returned. 125 // It returns an error if the duration conversion failed. 126 func parseDuration(s string) (time.Duration, error) { 127 if s == "" { 128 return time.Duration(-1), nil 129 } 130 131 b, err := strconv.ParseBool(s) 132 133 switch { 134 case err != nil: 135 return time.ParseDuration(s) 136 case b: 137 return time.Duration(-1), nil 138 } 139 140 return time.Duration(0), nil 141 } 142 143 // newContext returns a new context with timeout t if t > 0. 144 func newContext(t time.Duration) context.Context { 145 ctx := context.Background() 146 if t > 0 { 147 ctx, _ = context.WithTimeout(ctx, t) 148 } 149 return ctx 150 } 151 152 // getExitStatuses returns a map of the statuses of the pod. 153 func getExitStatuses(p *pkgPod.Pod) (map[string]int, error) { 154 _, manifest, err := p.PodManifest() 155 if err != nil { 156 return nil, err 157 } 158 159 stats := make(map[string]int) 160 for _, app := range manifest.Apps { 161 exitCode, err := p.AppExitCode(app.Name.String()) 162 if err != nil { 163 continue 164 } 165 stats[app.Name.String()] = exitCode 166 } 167 return stats, nil 168 } 169 170 // printStatus prints the pod's pid and per-app status codes 171 func printStatus(p *pkgPod.Pod) error { 172 if flagFormat != outputFormatTabbed { 173 pod, err := lib.NewPodFromInternalPod(p) 174 if err != nil { 175 return fmt.Errorf("error converting pod: %v", err) 176 } 177 switch flagFormat { 178 case outputFormatJSON: 179 result, err := json.Marshal(pod) 180 if err != nil { 181 return fmt.Errorf("error marshaling the pod: %v", err) 182 } 183 stdout.Print(string(result)) 184 case outputFormatPrettyJSON: 185 result, err := json.MarshalIndent(pod, "", "\t") 186 if err != nil { 187 return fmt.Errorf("error marshaling the pod: %v", err) 188 } 189 stdout.Print(string(result)) 190 } 191 return nil 192 } 193 194 state := p.State() 195 stdout.Printf("state=%s", state) 196 197 created, err := p.CreationTime() 198 if err != nil { 199 return fmt.Errorf("unable to get creation time for pod %q: %v", p.UUID, err) 200 } 201 createdStr := created.Format(defaultTimeLayout) 202 203 stdout.Printf("created=%s", createdStr) 204 205 started, err := p.StartTime() 206 if err != nil { 207 return fmt.Errorf("unable to get start time for pod %q: %v", p.UUID, err) 208 } 209 var startedStr string 210 if !started.IsZero() { 211 startedStr = started.Format(defaultTimeLayout) 212 stdout.Printf("started=%s", startedStr) 213 } 214 215 if state == pkgPod.Running || state == pkgPod.Exited { 216 stdout.Printf("networks=%s", fmtNets(p.Nets)) 217 } 218 219 if !(state == pkgPod.Running || state == pkgPod.Deleting || state == pkgPod.ExitedDeleting || state == pkgPod.Exited || state == pkgPod.ExitedGarbage) { 220 return nil 221 } 222 223 if pid, err := p.Pid(); err == nil { 224 // the pid file might not be written yet when the state changes to 'Running' 225 // it may also never be written if systemd never executes (e.g.: a bad command) 226 stdout.Printf("pid=%d", pid) 227 } 228 stdout.Printf("exited=%t", (state == pkgPod.Exited || state == pkgPod.ExitedGarbage)) 229 230 if state != pkgPod.Running { 231 stats, err := getExitStatuses(p) 232 if err != nil { 233 return fmt.Errorf("unable to get exit statuses for pod %q: %v", p.UUID, err) 234 } 235 for app, stat := range stats { 236 stdout.Printf("app-%s=%d", app, stat) 237 } 238 } 239 return nil 240 }