github.com/verrazzano/verrazzano@v1.7.0/tools/vz/cmd/analyze/analyze.go (about)

     1  // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package analyze
     5  
     6  import (
     7  	"fmt"
     8  	"github.com/spf13/cobra"
     9  	"github.com/spf13/pflag"
    10  	cmdhelpers "github.com/verrazzano/verrazzano/tools/vz/cmd/helpers"
    11  	"github.com/verrazzano/verrazzano/tools/vz/pkg/analysis"
    12  	vzbugreport "github.com/verrazzano/verrazzano/tools/vz/pkg/bugreport"
    13  	"github.com/verrazzano/verrazzano/tools/vz/pkg/constants"
    14  	"github.com/verrazzano/verrazzano/tools/vz/pkg/helpers"
    15  	"os"
    16  	"path/filepath"
    17  )
    18  
    19  const (
    20  	CommandName = "analyze"
    21  	helpShort   = "Analyze cluster"
    22  	helpLong    = `Analyze cluster for identifying issues and providing advice`
    23  	helpExample = `
    24  # Run analysis tool on captured directory
    25  vz analyze --capture-dir <path>
    26  
    27  # Run analysis tool on the live cluster
    28  vz analyze
    29  `
    30  )
    31  
    32  func NewCmdAnalyze(vzHelper helpers.VZHelper) *cobra.Command {
    33  	cmd := cmdhelpers.NewCommand(vzHelper, CommandName, helpShort, helpLong)
    34  	cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
    35  		return validateReportFormat(cmd)
    36  	}
    37  	cmd.RunE = func(cmd *cobra.Command, args []string) error {
    38  		return RunCmdAnalyze(cmd, vzHelper, true)
    39  	}
    40  
    41  	cmd.Example = helpExample
    42  	cmd.PersistentFlags().String(constants.DirectoryFlagName, constants.DirectoryFlagValue, constants.DirectoryFlagUsage)
    43  	cmd.PersistentFlags().String(constants.ReportFileFlagName, constants.ReportFileFlagValue, constants.ReportFileFlagUsage)
    44  	cmd.PersistentFlags().String(constants.ReportFormatFlagName, constants.SummaryReport, constants.ReportFormatFlagUsage)
    45  	cmd.PersistentFlags().BoolP(constants.VerboseFlag, constants.VerboseFlagShorthand, constants.VerboseFlagDefault, constants.VerboseFlagUsage)
    46  
    47  	// Verifies that the CLI args are not set at the creation of a command
    48  	vzHelper.VerifyCLIArgsNil(cmd)
    49  
    50  	return cmd
    51  }
    52  
    53  // analyzeLiveCluster Analyzes live cluster by capturing the snapshot, when capture-dir is not set
    54  func analyzeLiveCluster(cmd *cobra.Command, vzHelper helpers.VZHelper, directory string) error {
    55  	// Get the kubernetes clientset, which will validate that the kubeconfig and context are valid.
    56  	kubeClient, err := vzHelper.GetKubeClient(cmd)
    57  	if err != nil {
    58  		return err
    59  	}
    60  
    61  	// Get the dynamic client to retrieve OAM resources
    62  	dynamicClient, err := vzHelper.GetDynamicClient(cmd)
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	// Get the controller runtime client
    68  	client, err := vzHelper.GetClient(cmd)
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	// Create a directory for the analyze command
    74  	reportDirectory := filepath.Join(directory, constants.BugReportRoot)
    75  	err = os.MkdirAll(reportDirectory, os.ModePerm)
    76  	if err != nil {
    77  		return fmt.Errorf("an error occurred while creating the directory %s: %s", reportDirectory, err.Error())
    78  	}
    79  
    80  	// Get the list of namespaces with label verrazzano-managed=true, where the applications are deployed
    81  	moreNS := helpers.GetVZManagedNamespaces(kubeClient)
    82  
    83  	// Instruct the helper to display the message for analyzing the live cluster
    84  	helpers.SetIsLiveCluster()
    85  
    86  	// Capture cluster snapshot
    87  	podLogs := vzbugreport.PodLogs{
    88  		IsPodLog: true,
    89  		Duration: int64(0),
    90  	}
    91  	clusterSnapshotCtx := helpers.ClusterSnapshotCtx{BugReportDir: reportDirectory, MoreNS: moreNS, PrintReportToConsole: true}
    92  	return vzbugreport.CaptureClusterSnapshot(kubeClient, dynamicClient, client, vzHelper, podLogs, clusterSnapshotCtx)
    93  }
    94  
    95  func RunCmdAnalyze(cmd *cobra.Command, vzHelper helpers.VZHelper, printReportToConsole bool) error {
    96  	directoryFlag := cmd.PersistentFlags().Lookup(constants.DirectoryFlagName)
    97  	if err := setVzK8sVersion(directoryFlag, vzHelper, cmd); err == nil {
    98  		fmt.Fprintf(vzHelper.GetOutputStream(), helpers.GetVersionOut())
    99  	}
   100  	reportFileName, err := cmd.PersistentFlags().GetString(constants.ReportFileFlagName)
   101  	if err != nil {
   102  		fmt.Fprintf(vzHelper.GetOutputStream(), "error getting the report file name: %s", err.Error())
   103  	}
   104  	reportFormat := getReportFormat(cmd)
   105  
   106  	// set the flag to control the display the resources captured
   107  	isVerbose, err := cmd.PersistentFlags().GetBool(constants.VerboseFlag)
   108  	if err != nil {
   109  		return fmt.Errorf("an error occurred while reading value for the flag %s: %s", constants.VerboseFlag, err.Error())
   110  	}
   111  	helpers.SetVerboseOutput(isVerbose)
   112  	directory := ""
   113  	if directoryFlag == nil || directoryFlag.Value.String() == "" {
   114  		// Create a temporary directory to place the generated files, which will also be the input for analyze command
   115  		directory, err = os.MkdirTemp("", constants.BugReportDir)
   116  		if err != nil {
   117  			return fmt.Errorf("an error occurred while creating the directory to place cluster resources: %s", err.Error())
   118  		}
   119  		defer os.RemoveAll(directory)
   120  		if err := analyzeLiveCluster(cmd, vzHelper, directory); err != nil {
   121  			return err
   122  		}
   123  	} else {
   124  		directory, err = cmd.PersistentFlags().GetString(constants.DirectoryFlagName)
   125  		if err != nil {
   126  			fmt.Fprintf(vzHelper.GetOutputStream(), "error fetching flags: %s", err.Error())
   127  		}
   128  	}
   129  	return analysis.AnalysisMain(vzHelper, directory, reportFileName, reportFormat, printReportToConsole)
   130  }
   131  
   132  // setVzK8sVersion sets vz and k8s version
   133  func setVzK8sVersion(directoryFlag *pflag.Flag, vzHelper helpers.VZHelper, cmd *cobra.Command) error {
   134  	if directoryFlag == nil || directoryFlag.Value.String() == "" {
   135  		// Get the controller runtime client
   136  		client, err := vzHelper.GetClient(cmd)
   137  		if err != nil {
   138  			return err
   139  		}
   140  		// set vz version
   141  		if err := helpers.SetVzVer(&client); err != nil {
   142  			return err
   143  		}
   144  		// set cluster k8s version
   145  		if err := helpers.SetK8sVer(); err != nil {
   146  			return err
   147  		}
   148  		// print k8s and vz version on console stdout
   149  		return nil
   150  	}
   151  	return fmt.Errorf("cannot set vz and k8s version")
   152  }
   153  
   154  // validateReportFormat validates the value specified for flag report-format
   155  func validateReportFormat(cmd *cobra.Command) error {
   156  	reportFormatValue := getReportFormat(cmd)
   157  	switch reportFormatValue {
   158  	case constants.SummaryReport, constants.DetailedReport:
   159  		return nil
   160  	default:
   161  		return fmt.Errorf("%q is not valid for flag report-format, only %q and %q are valid", reportFormatValue, constants.SummaryReport, constants.DetailedReport)
   162  	}
   163  }
   164  
   165  // getReportFormat returns the value set for flag report-format
   166  func getReportFormat(cmd *cobra.Command) string {
   167  	reportFormat := cmd.PersistentFlags().Lookup(constants.ReportFormatFlagName)
   168  	if reportFormat == nil {
   169  		return constants.SummaryReport
   170  	}
   171  	return reportFormat.Value.String()
   172  }