github.com/verrazzano/verrazzano@v1.7.1/tools/vz/pkg/internal/util/cluster/analyzer.go (about)

     1  // Copyright (c) 2021, 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 cluster handles cluster analysis
     5  package cluster
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"regexp"
    11  
    12  	"github.com/verrazzano/verrazzano/tools/vz/pkg/helpers"
    13  	"github.com/verrazzano/verrazzano/tools/vz/pkg/internal/util/files"
    14  	"github.com/verrazzano/verrazzano/tools/vz/pkg/internal/util/report"
    15  	"go.uber.org/zap"
    16  )
    17  
    18  // TBD: Overall the intention/design is that we could execute analysis in parallel if we want to do that in the
    19  //
    20  //	future. So in general analyzers are independent of each other and thread safe, and not expecting to
    21  //	be executed in a particular order.
    22  //	However, there may be special cases where we want an analysis to be done and information gleaned
    23  //	from that analysis to be available to other analyzers. For example, the analysis of the state
    24  //	of Verrazzano is something that is likely to fall into that category. It will make a high level
    25  //	determination of where in the lifecycle we are at, and other analyzers may need to easily get that
    26  //	information to give better guidance on the issues/actions.
    27  //
    28  //	The current implementation is calling the analyzers serially in order.
    29  //	If we do decide to handle analysis in a parallel fashion later, we likely will need to have some
    30  //	analyzers called deterministically in exact order before we fire off other analyzers in parallel.
    31  //	So we may break this into 2 lists in the future: serial analysis functions, parallel analysis functions
    32  //	Analyzers that may fall into this category should be annotated, with a comment, there currently is only
    33  //	one that may require that.
    34  
    35  // These are the high level analysis functions that are called. The "Runtime Issues" maps to only certificate functions currently.
    36  var clusterAnalysisFunctions = map[string]func(log *zap.SugaredLogger, directory string) (err error){
    37  	"Verrazzano Status":                         AnalyzeVerrazzano, // Execute first, this may share data other analyzers can use
    38  	"Pod Related Issues":                        AnalyzePodIssues,
    39  	"Rancher Status":                            AnalyzeRancher,
    40  	"Runtime Issues":                            AnalyzeCertificateRelatedIssues,
    41  	"Cluster API Issues":                        AnalyzeClusterAPI,
    42  	"Networking Issues":                         AnalyzeNetworkingIssues,
    43  	"Finalizer and Resource Termination Issues": AnalyzeNamespaceRelatedIssues,
    44  	"MySQL Issues":                              AnalyzeMySQLRelatedIssues,
    45  }
    46  
    47  // ClusterDumpDirectoriesRe is used for finding cluster-snapshot directory name matches
    48  var ClusterDumpDirectoriesRe = regexp.MustCompile(`.*/cluster-snapshot$`)
    49  
    50  // LogFilesMatchRe is used for finding pod log files in a cluster dump
    51  var LogFilesMatchRe = regexp.MustCompile(`logs.txt`)
    52  
    53  // PodFilesMatchRe is used for finding pod files in a cluster dump
    54  var PodFilesMatchRe = regexp.MustCompile(`pods.json`)
    55  
    56  // ErrorSearchRe is used for searching for case insensitive "error". This is useful when we know there is a
    57  // problem lurking but we can't identify the specific issue and are trying to capture relevant information
    58  // to include in support data from logs and events
    59  var ErrorSearchRe = regexp.MustCompile(`(?i).*error.*`)
    60  
    61  // WideErrorSearchRe is used for casting a wider net while looking for issues TBD: .*ERROR.*|.*Error.*|.*FAILED.*
    62  var WideErrorSearchRe = regexp.MustCompile(`(?i).*error.*|.*failed.*`)
    63  
    64  // EventReasonFailedRe is used for finding event reason failures
    65  var EventReasonFailedRe = regexp.MustCompile(`.*Failed.*`)
    66  
    67  // RunAnalysis is the main entry analysis function
    68  func RunAnalysis(vzHelper helpers.VZHelper, log *zap.SugaredLogger, rootDirectory string) (err error) {
    69  	log.Debugf("Cluster Analyzer runAnalysis on %s", rootDirectory)
    70  
    71  	clusterRoots, err := files.GetMatchingDirectoryNames(log, rootDirectory, ClusterDumpDirectoriesRe)
    72  	if err != nil {
    73  		log.Debugf("Cluster Analyzer runAnalysis failed examining directories for %s", rootDirectory, err)
    74  		return fmt.Errorf("Cluster Analyzer runAnalysis failed examining directories for %s", rootDirectory)
    75  	}
    76  	if len(clusterRoots) == 0 {
    77  		log.Debugf("Cluster Analyzer runAnalysis didn't find any clusters to analyze for %s", rootDirectory)
    78  		return fmt.Errorf("Cluster Analyzer runAnalysis didn't find any clusters to analyze for %s", rootDirectory)
    79  	}
    80  
    81  	for _, clusterRoot := range clusterRoots {
    82  		// Ignore directories if they don't contain snapshots. Checking if verrazzano-resources.json exists in the dir
    83  		// that implies the directory has the required snapshots.
    84  		vzResourcesPath := files.FormFilePathInClusterRoot(clusterRoot, verrazzanoResource)
    85  		fileInfo, e := os.Stat(vzResourcesPath)
    86  		if e != nil || fileInfo.Size() == 0 {
    87  			log.Debugf("Verrazzano resource file %s is either empty or not there", vzResourcesPath)
    88  			continue
    89  		}
    90  		analyzeCluster(vzHelper, log, clusterRoot)
    91  	}
    92  
    93  	return nil
    94  }
    95  
    96  func analyzeCluster(vzHelper helpers.VZHelper, log *zap.SugaredLogger, clusterRoot string) (err error) {
    97  	log.Debugf("analyzeCluster called for %s", clusterRoot)
    98  	report.AddSourceAnalyzed(clusterRoot)
    99  
   100  	for functionName, function := range clusterAnalysisFunctions {
   101  		err := function(log, clusterRoot)
   102  		if err != nil {
   103  			// Log the error and continue on
   104  			fmt.Fprintf(vzHelper.GetErrorStream(), fmt.Sprintf("Error processing analysis function %s\n", functionName), err)
   105  		}
   106  	}
   107  
   108  	return nil
   109  }