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 }