github.com/verrazzano/verrazzano@v1.7.1/tools/vz/cmd/analyze/analyze.go (about) 1 // Copyright (c) 2022, 2024, 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 "os" 9 "path/filepath" 10 11 "github.com/spf13/cobra" 12 cmdhelpers "github.com/verrazzano/verrazzano/tools/vz/cmd/helpers" 13 "github.com/verrazzano/verrazzano/tools/vz/pkg/analysis" 14 vzbugreport "github.com/verrazzano/verrazzano/tools/vz/pkg/bugreport" 15 "github.com/verrazzano/verrazzano/tools/vz/pkg/constants" 16 "github.com/verrazzano/verrazzano/tools/vz/pkg/helpers" 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 type directoryAndTarValidationStruct struct { 33 directory string 34 tarFile string 35 reportFile string 36 isVerbose bool 37 } 38 39 func NewCmdAnalyze(vzHelper helpers.VZHelper) *cobra.Command { 40 cmd := cmdhelpers.NewCommand(vzHelper, CommandName, helpShort, helpLong) 41 cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { 42 return validateReportFormat(cmd) 43 } 44 cmd.RunE = func(cmd *cobra.Command, args []string) error { 45 return RunCmdAnalyze(cmd, vzHelper, true) 46 } 47 48 cmd.Example = helpExample 49 cmd.PersistentFlags().String(constants.DirectoryFlagName, constants.DirectoryFlagValue, constants.DirectoryFlagUsage) 50 cmd.PersistentFlags().String(constants.ReportFileFlagName, constants.ReportFileFlagValue, constants.ReportFileFlagUsage) 51 cmd.PersistentFlags().String(constants.TarFileFlagName, constants.TarFileFlagValue, constants.TarFileFlagUsage) 52 cmd.PersistentFlags().String(constants.ReportFormatFlagName, constants.SummaryReport, constants.ReportFormatFlagUsage) 53 cmd.PersistentFlags().BoolP(constants.VerboseFlag, constants.VerboseFlagShorthand, constants.VerboseFlagDefault, constants.VerboseFlagUsage) 54 55 // Verifies that the CLI args are not set at the creation of a command 56 vzHelper.VerifyCLIArgsNil(cmd) 57 58 return cmd 59 } 60 61 // analyzeLiveCluster Analyzes live cluster by capturing the snapshot, when capture-dir is not set 62 func analyzeLiveCluster(cmd *cobra.Command, vzHelper helpers.VZHelper, directory string) error { 63 // Get the kubernetes clientset, which will validate that the kubeconfig and context are valid. 64 kubeClient, err := vzHelper.GetKubeClient(cmd) 65 if err != nil { 66 return err 67 } 68 69 // Get the dynamic client to retrieve OAM resources 70 dynamicClient, err := vzHelper.GetDynamicClient(cmd) 71 if err != nil { 72 return err 73 } 74 75 // Get the controller runtime client 76 client, err := vzHelper.GetClient(cmd) 77 if err != nil { 78 return err 79 } 80 81 // Create a directory for the analyze command 82 reportDirectory := filepath.Join(directory, constants.BugReportRoot) 83 err = os.MkdirAll(reportDirectory, os.ModePerm) 84 if err != nil { 85 return fmt.Errorf("an error occurred while creating the directory %s: %s", reportDirectory, err.Error()) 86 } 87 88 // Get the list of namespaces with label verrazzano-managed=true, where the applications are deployed 89 moreNS := helpers.GetVZManagedNamespaces(kubeClient) 90 91 // Instruct the helper to display the message for analyzing the live cluster 92 helpers.SetIsLiveCluster() 93 94 // Capture cluster snapshot 95 podLogs := helpers.PodLogs{ 96 IsPodLog: true, 97 Duration: int64(0), 98 } 99 clusterSnapshotCtx := helpers.ClusterSnapshotCtx{BugReportDir: reportDirectory, MoreNS: moreNS, PrintReportToConsole: true} 100 return vzbugreport.CaptureClusterSnapshot(kubeClient, dynamicClient, client, vzHelper, podLogs, clusterSnapshotCtx) 101 } 102 103 func RunCmdAnalyze(cmd *cobra.Command, vzHelper helpers.VZHelper, printReportToConsole bool) error { 104 validatedStruct, err := parseFlags(cmd, vzHelper, constants.DirectoryFlagName, constants.TarFileFlagName, constants.ReportFileFlagName, constants.VerboseFlag) 105 if err != nil { 106 return err 107 } 108 reportFormat := getReportFormat(cmd) 109 110 // set the flag to control the display the resources captured 111 helpers.SetVerboseOutput(validatedStruct.isVerbose) 112 if validatedStruct.directory == "" { 113 // Create a temporary directory to place the generated files, which will also be the input for analyze command 114 validatedStruct.directory, err = os.MkdirTemp("", constants.BugReportDir) 115 defer os.RemoveAll(validatedStruct.directory) 116 if err != nil { 117 return fmt.Errorf("an error occurred while creating the directory to place cluster resources: %s", err.Error()) 118 } 119 120 if validatedStruct.tarFile == "" { 121 if err := analyzeLiveCluster(cmd, vzHelper, validatedStruct.directory); err != nil { 122 return err 123 } 124 } else { 125 //This is the case where only the tar string is specified 126 file, err := os.Open(validatedStruct.tarFile) 127 defer file.Close() 128 if err != nil { 129 return fmt.Errorf("an error occurred when trying to open %s: %s", validatedStruct.tarFile, err.Error()) 130 } 131 err = helpers.UntarArchive(validatedStruct.directory, file) 132 if err != nil { 133 return fmt.Errorf("an error occurred while trying to untar %s: %s", validatedStruct.tarFile, err.Error()) 134 } 135 } 136 } 137 return analysis.AnalysisMain(vzHelper, validatedStruct.directory, validatedStruct.reportFile, reportFormat, printReportToConsole) 138 } 139 140 // This function validates the directory and tar file flags along with checking that the directory flag and the tar file are not both specified 141 func parseFlags(cmd *cobra.Command, vzHelper helpers.VZHelper, directoryFlagValue string, tarFlagValue string, reportFileFlagValue string, verboseFlagValue string) (*directoryAndTarValidationStruct, error) { 142 directory, err := cmd.PersistentFlags().GetString(directoryFlagValue) 143 if err != nil { 144 return nil, fmt.Errorf(constants.FlagErrorMessage, constants.DirectoryFlagName, err.Error()) 145 } 146 tarFileString, err := cmd.PersistentFlags().GetString(tarFlagValue) 147 if err != nil { 148 return nil, fmt.Errorf(constants.FlagErrorMessage, constants.TarFileFlagName, err.Error()) 149 } 150 if directory != "" && tarFileString != "" { 151 return nil, fmt.Errorf("a directory and a tar file cannot be both specified") 152 } 153 isVerbose, err := cmd.PersistentFlags().GetBool(verboseFlagValue) 154 if err != nil { 155 return nil, fmt.Errorf(constants.FlagErrorMessage, constants.VerboseFlag, err.Error()) 156 } 157 if err := setVzK8sVersion(tarFileString, directory, vzHelper, cmd); err == nil { 158 fmt.Fprintf(vzHelper.GetOutputStream(), helpers.GetVersionOut()) 159 } 160 reportFileName, err := cmd.PersistentFlags().GetString(reportFileFlagValue) 161 if err != nil { 162 fmt.Fprintf(vzHelper.GetOutputStream(), "error getting the report file name: %s", err.Error()) 163 } 164 return &directoryAndTarValidationStruct{directory: directory, tarFile: tarFileString, reportFile: reportFileName, isVerbose: isVerbose}, nil 165 } 166 167 // setVzK8sVersion sets vz and k8s version 168 func setVzK8sVersion(tarFileString string, directory string, vzHelper helpers.VZHelper, cmd *cobra.Command) error { 169 if directory == "" && tarFileString == "" { 170 // Get the controller runtime client 171 client, err := vzHelper.GetClient(cmd) 172 if err != nil { 173 return err 174 } 175 // set vz version 176 if err := helpers.SetVzVer(&client); err != nil { 177 return err 178 } 179 // set cluster k8s version 180 if err := helpers.SetK8sVer(); err != nil { 181 return err 182 } 183 // print k8s and vz version on console stdout 184 return nil 185 } 186 return fmt.Errorf("cannot set vz and k8s version") 187 } 188 189 // validateReportFormat validates the value specified for flag report-format 190 func validateReportFormat(cmd *cobra.Command) error { 191 reportFormatValue := getReportFormat(cmd) 192 switch reportFormatValue { 193 case constants.SummaryReport, constants.DetailedReport: 194 return nil 195 default: 196 return fmt.Errorf("%q is not valid for flag report-format, only %q and %q are valid", reportFormatValue, constants.SummaryReport, constants.DetailedReport) 197 } 198 } 199 200 // getReportFormat returns the value set for flag report-format 201 func getReportFormat(cmd *cobra.Command) string { 202 reportFormat := cmd.PersistentFlags().Lookup(constants.ReportFormatFlagName) 203 if reportFormat == nil { 204 return constants.SummaryReport 205 } 206 return reportFormat.Value.String() 207 }