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

     1  // Copyright (c) 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  package cluster
     4  
     5  import (
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"time"
    10  
    11  	"github.com/verrazzano/verrazzano/tools/vz/pkg/constants"
    12  	"github.com/verrazzano/verrazzano/tools/vz/pkg/internal/util/files"
    13  	"github.com/verrazzano/verrazzano/tools/vz/pkg/internal/util/report"
    14  	"go.uber.org/zap"
    15  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    16  )
    17  
    18  // AnalyzeMySQLRelatedIssues is the initial entry function for mySQL related issues, and it returns an error.
    19  // It checks to see whether an innoDBCluster is in a state of terminating, and reports an issue based on the length of its termination
    20  func AnalyzeMySQLRelatedIssues(log *zap.SugaredLogger, clusterRoot string) (err error) {
    21  	allNamespacesFound, err := files.FindNamespaces(log, clusterRoot)
    22  	if err != nil {
    23  		return err
    24  	}
    25  	var issueReporter = report.IssueReporter{
    26  		PendingIssues: make(map[string]report.Issue),
    27  	}
    28  	timeOfCapture, err := files.GetTimeOfCapture(log, clusterRoot)
    29  	if err != nil {
    30  		return err
    31  	}
    32  	for _, namespace := range allNamespacesFound {
    33  		if namespace != "keycloak" {
    34  			continue
    35  		}
    36  		innoDBClusterFile := files.FormFilePathInNamespace(clusterRoot, namespace, constants.InnoDBClusterJSON)
    37  		innoDBResourceList, err := getInnoDBClusterResources(log, innoDBClusterFile)
    38  		if err != nil {
    39  			return err
    40  		}
    41  		if innoDBResourceList == nil {
    42  			continue
    43  		}
    44  		for _, item := range innoDBResourceList.Items {
    45  			isTerminating, message := isInnoDBClusterCurrentlyInTerminatingStatus(&item, timeOfCapture)
    46  			if isTerminating {
    47  				reportInnoDBClustersInTerminatingStatusIssue(clusterRoot, &issueReporter, innoDBClusterFile, message)
    48  
    49  			}
    50  		}
    51  
    52  	}
    53  
    54  	issueReporter.Contribute(log, clusterRoot)
    55  	return nil
    56  }
    57  
    58  // getInnoDBClusterResource returns the InnoDBCluster list that is in the inno-db-cluster.json file
    59  func getInnoDBClusterResources(log *zap.SugaredLogger, path string) (innoDBClusterObject *unstructured.UnstructuredList, err error) {
    60  	resourceToReturn := unstructured.UnstructuredList{}
    61  	file, err := os.Open(path)
    62  	defer file.Close()
    63  	if err != nil {
    64  		log.Debugf("file %s not found", path)
    65  		return nil, nil
    66  	}
    67  	fileBytes, err := io.ReadAll(file)
    68  	if err != nil {
    69  		log.Errorf("Failed reading namespace.json file %s", path)
    70  		return nil, err
    71  	}
    72  	err = resourceToReturn.UnmarshalJSON(fileBytes)
    73  	if err != nil {
    74  		log.Errorf("Failed to unmarshal namespace resource at %s", path)
    75  		return nil, err
    76  	}
    77  	return &resourceToReturn, err
    78  }
    79  
    80  // isInnoDBClusterCurrentlyInTerminatingStatus checks if an innoDBCluster resource has been in a state of deletion for 10 minutes or greater
    81  func isInnoDBClusterCurrentlyInTerminatingStatus(innoDBClusterResource *unstructured.Unstructured, timeOfCapture *time.Time) (bool, string) {
    82  	var deletionMessage string
    83  	deletionTimestamp := innoDBClusterResource.GetDeletionTimestamp()
    84  	if deletionTimestamp == nil || timeOfCapture == nil {
    85  		return false, deletionMessage
    86  	}
    87  	diff := timeOfCapture.Sub(deletionTimestamp.Time)
    88  	if int(diff.Minutes()) < 10 {
    89  		return false, deletionMessage
    90  	}
    91  	deletionMessage = "The innoDBClusterResource " + innoDBClusterResource.GetName() + " has spent " + fmt.Sprint(int(diff.Minutes())) + " minutes and " + fmt.Sprint(int(diff.Seconds())%60) + " seconds deleting"
    92  	return true, deletionMessage
    93  }
    94  
    95  // reportInnoDBClustersInTerminatingStatusIssue is a helper function that reports an issue if a innoDBCluster resource has been in a state of deletion for more than 10 minutes
    96  func reportInnoDBClustersInTerminatingStatusIssue(clusterRoot string, issueReporter *report.IssueReporter, InnoDBClusterFile string, message string) {
    97  	files := []string{InnoDBClusterFile}
    98  	messageList := []string{message}
    99  	issueReporter.AddKnownIssueMessagesFiles(report.InnoDBClusterResourceCurrentlyInTerminatingStateForLongDuration, clusterRoot, messageList, files)
   100  
   101  }