github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/cmd/helm/status.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"log"
    23  	"strings"
    24  	"time"
    25  
    26  	"github.com/spf13/cobra"
    27  
    28  	"github.com/stefanmcshane/helm/cmd/helm/require"
    29  	"github.com/stefanmcshane/helm/pkg/action"
    30  	"github.com/stefanmcshane/helm/pkg/chartutil"
    31  	"github.com/stefanmcshane/helm/pkg/cli/output"
    32  	"github.com/stefanmcshane/helm/pkg/release"
    33  )
    34  
    35  // NOTE: Keep the list of statuses up-to-date with pkg/release/status.go.
    36  var statusHelp = `
    37  This command shows the status of a named release.
    38  The status consists of:
    39  - last deployment time
    40  - k8s namespace in which the release lives
    41  - state of the release (can be: unknown, deployed, uninstalled, superseded, failed, uninstalling, pending-install, pending-upgrade or pending-rollback)
    42  - revision of the release
    43  - description of the release (can be completion message or error message, need to enable --show-desc)
    44  - list of resources that this release consists of, sorted by kind
    45  - details on last test suite run, if applicable
    46  - additional notes provided by the chart
    47  `
    48  
    49  func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
    50  	client := action.NewStatus(cfg)
    51  	var outfmt output.Format
    52  
    53  	cmd := &cobra.Command{
    54  		Use:   "status RELEASE_NAME",
    55  		Short: "display the status of the named release",
    56  		Long:  statusHelp,
    57  		Args:  require.ExactArgs(1),
    58  		ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
    59  			if len(args) != 0 {
    60  				return nil, cobra.ShellCompDirectiveNoFileComp
    61  			}
    62  			return compListReleases(toComplete, args, cfg)
    63  		},
    64  		RunE: func(cmd *cobra.Command, args []string) error {
    65  			rel, err := client.Run(args[0])
    66  			if err != nil {
    67  				return err
    68  			}
    69  
    70  			// strip chart metadata from the output
    71  			rel.Chart = nil
    72  
    73  			return outfmt.Write(out, &statusPrinter{rel, false, client.ShowDescription})
    74  		},
    75  	}
    76  
    77  	f := cmd.Flags()
    78  
    79  	f.IntVar(&client.Version, "revision", 0, "if set, display the status of the named release with revision")
    80  
    81  	err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
    82  		if len(args) == 1 {
    83  			return compListRevisions(toComplete, cfg, args[0])
    84  		}
    85  		return nil, cobra.ShellCompDirectiveNoFileComp
    86  	})
    87  
    88  	if err != nil {
    89  		log.Fatal(err)
    90  	}
    91  
    92  	bindOutputFlag(cmd, &outfmt)
    93  	f.BoolVar(&client.ShowDescription, "show-desc", false, "if set, display the description message of the named release")
    94  
    95  	return cmd
    96  }
    97  
    98  type statusPrinter struct {
    99  	release         *release.Release
   100  	debug           bool
   101  	showDescription bool
   102  }
   103  
   104  func (s statusPrinter) WriteJSON(out io.Writer) error {
   105  	return output.EncodeJSON(out, s.release)
   106  }
   107  
   108  func (s statusPrinter) WriteYAML(out io.Writer) error {
   109  	return output.EncodeYAML(out, s.release)
   110  }
   111  
   112  func (s statusPrinter) WriteTable(out io.Writer) error {
   113  	if s.release == nil {
   114  		return nil
   115  	}
   116  	fmt.Fprintf(out, "NAME: %s\n", s.release.Name)
   117  	if !s.release.Info.LastDeployed.IsZero() {
   118  		fmt.Fprintf(out, "LAST DEPLOYED: %s\n", s.release.Info.LastDeployed.Format(time.ANSIC))
   119  	}
   120  	fmt.Fprintf(out, "NAMESPACE: %s\n", s.release.Namespace)
   121  	fmt.Fprintf(out, "STATUS: %s\n", s.release.Info.Status.String())
   122  	fmt.Fprintf(out, "REVISION: %d\n", s.release.Version)
   123  	if s.showDescription {
   124  		fmt.Fprintf(out, "DESCRIPTION: %s\n", s.release.Info.Description)
   125  	}
   126  
   127  	executions := executionsByHookEvent(s.release)
   128  	if tests, ok := executions[release.HookTest]; !ok || len(tests) == 0 {
   129  		fmt.Fprintln(out, "TEST SUITE: None")
   130  	} else {
   131  		for _, h := range tests {
   132  			// Don't print anything if hook has not been initiated
   133  			if h.LastRun.StartedAt.IsZero() {
   134  				continue
   135  			}
   136  			fmt.Fprintf(out, "TEST SUITE:     %s\n%s\n%s\n%s\n",
   137  				h.Name,
   138  				fmt.Sprintf("Last Started:   %s", h.LastRun.StartedAt.Format(time.ANSIC)),
   139  				fmt.Sprintf("Last Completed: %s", h.LastRun.CompletedAt.Format(time.ANSIC)),
   140  				fmt.Sprintf("Phase:          %s", h.LastRun.Phase),
   141  			)
   142  		}
   143  	}
   144  
   145  	if s.debug {
   146  		fmt.Fprintln(out, "USER-SUPPLIED VALUES:")
   147  		err := output.EncodeYAML(out, s.release.Config)
   148  		if err != nil {
   149  			return err
   150  		}
   151  		// Print an extra newline
   152  		fmt.Fprintln(out)
   153  
   154  		cfg, err := chartutil.CoalesceValues(s.release.Chart, s.release.Config)
   155  		if err != nil {
   156  			return err
   157  		}
   158  
   159  		fmt.Fprintln(out, "COMPUTED VALUES:")
   160  		err = output.EncodeYAML(out, cfg.AsMap())
   161  		if err != nil {
   162  			return err
   163  		}
   164  		// Print an extra newline
   165  		fmt.Fprintln(out)
   166  	}
   167  
   168  	if strings.EqualFold(s.release.Info.Description, "Dry run complete") || s.debug {
   169  		fmt.Fprintln(out, "HOOKS:")
   170  		for _, h := range s.release.Hooks {
   171  			fmt.Fprintf(out, "---\n# Source: %s\n%s\n", h.Path, h.Manifest)
   172  		}
   173  		fmt.Fprintf(out, "MANIFEST:\n%s\n", s.release.Manifest)
   174  	}
   175  
   176  	if len(s.release.Info.Notes) > 0 {
   177  		fmt.Fprintf(out, "NOTES:\n%s\n", strings.TrimSpace(s.release.Info.Notes))
   178  	}
   179  	return nil
   180  }
   181  
   182  func executionsByHookEvent(rel *release.Release) map[release.HookEvent][]*release.Hook {
   183  	result := make(map[release.HookEvent][]*release.Hook)
   184  	for _, h := range rel.Hooks {
   185  		for _, e := range h.Events {
   186  			executions, ok := result[e]
   187  			if !ok {
   188  				executions = []*release.Hook{}
   189  			}
   190  			result[e] = append(executions, h)
   191  		}
   192  	}
   193  	return result
   194  }