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  }