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

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