github.com/verrazzano/verrazzano@v1.7.1/tools/vz/pkg/internal/util/cluster/namespaces.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 4 // Package cluster handles cluster analysis 5 package cluster 6 7 import ( 8 encjson "encoding/json" 9 "fmt" 10 "io" 11 "os" 12 "time" 13 14 "github.com/verrazzano/verrazzano/tools/vz/pkg/constants" 15 "github.com/verrazzano/verrazzano/tools/vz/pkg/internal/util/files" 16 "github.com/verrazzano/verrazzano/tools/vz/pkg/internal/util/report" 17 "go.uber.org/zap" 18 corev1 "k8s.io/api/core/v1" 19 ) 20 21 // AnalyzeNamespaceRelatedIssues is the initial entry function for namespace related issues, and it returns an error. 22 // It checks to see whether the namespace being analyzed is in a state of terminating 23 func AnalyzeNamespaceRelatedIssues(log *zap.SugaredLogger, clusterRoot string) (err error) { 24 allNamespacesFound, err = files.FindNamespaces(log, clusterRoot) 25 if err != nil { 26 return err 27 } 28 var issueReporter = report.IssueReporter{ 29 PendingIssues: make(map[string]report.Issue), 30 } 31 timeOfCapture, err := files.GetTimeOfCapture(log, clusterRoot) 32 if err != nil { 33 return err 34 } 35 for _, namespace := range allNamespacesFound { 36 namespaceFile := files.FormFilePathInNamespace(clusterRoot, namespace, constants.NamespaceJSON) 37 namespaceObject, err := getNamespaceResource(log, namespaceFile) 38 if err != nil { 39 return err 40 } 41 if namespaceObject == nil { 42 continue 43 } 44 issueFound, messageList := isNamespaceCurrentlyInTerminatingStatus(namespaceObject, timeOfCapture) 45 if issueFound { 46 reportNamespaceInTerminatingStatusIssue(clusterRoot, *namespaceObject, &issueReporter, namespaceFile, messageList) 47 } 48 49 } 50 51 issueReporter.Contribute(log, clusterRoot) 52 return nil 53 } 54 55 // getNamespaceResource returns the namespace object that is in the namespace file 56 func getNamespaceResource(log *zap.SugaredLogger, path string) (namespaceObject *corev1.Namespace, err error) { 57 namespaceResource := &corev1.Namespace{} 58 file, err := os.Open(path) 59 if err != nil { 60 log.Debug("file %s not found", path) 61 return nil, nil 62 } 63 defer file.Close() 64 fileBytes, err := io.ReadAll(file) 65 if err != nil { 66 log.Error("Failed reading namespace.json file %s", path) 67 return nil, err 68 } 69 err = encjson.Unmarshal(fileBytes, &namespaceResource) 70 if err != nil { 71 log.Error("Failed to unmarshal namespace resource at %s", path) 72 return nil, err 73 } 74 return namespaceResource, err 75 } 76 77 // isNamespaceCurrentlyInTerminatingStatus checks to see if that is the namespace currently has a status of terminating 78 func isNamespaceCurrentlyInTerminatingStatus(namespaceObject *corev1.Namespace, timeOfCapture *time.Time) (bool, []string) { 79 var listOfMessagesFromRelevantConditions = []string{} 80 if namespaceObject.Status.Phase != corev1.NamespaceTerminating { 81 return false, listOfMessagesFromRelevantConditions 82 } 83 var deletionMessage string 84 if namespaceObject.DeletionTimestamp == nil || timeOfCapture == nil { 85 return false, listOfMessagesFromRelevantConditions 86 } 87 diff := timeOfCapture.Sub(namespaceObject.DeletionTimestamp.Time) 88 if int(diff.Minutes()) < 10 { 89 return false, listOfMessagesFromRelevantConditions 90 } 91 deletionMessage = "The namespace " + namespaceObject.Name + " has spent " + fmt.Sprint(int(diff.Minutes())) + " minutes and " + fmt.Sprint(int(diff.Seconds())%60) + " seconds deleting" 92 listOfMessagesFromRelevantConditions = append(listOfMessagesFromRelevantConditions, deletionMessage) 93 namespaceConditions := namespaceObject.Status.Conditions 94 if namespaceConditions == nil { 95 return true, listOfMessagesFromRelevantConditions 96 } 97 for i := range namespaceConditions { 98 if namespaceConditions[i].Type == corev1.NamespaceFinalizersRemaining || namespaceConditions[i].Type == corev1.NamespaceContentRemaining { 99 listOfMessagesFromRelevantConditions = append(listOfMessagesFromRelevantConditions, namespaceConditions[i].Message) 100 } 101 } 102 return true, listOfMessagesFromRelevantConditions 103 } 104 func reportNamespaceInTerminatingStatusIssue(clusterRoot string, namespace corev1.Namespace, issueReporter *report.IssueReporter, namespaceFile string, messagesFromConditions []string) { 105 files := []string{namespaceFile} 106 issueReporter.AddKnownIssueMessagesFiles(report.NamespaceCurrentlyInTerminatingStateForLongDuration, clusterRoot, messagesFromConditions, files) 107 108 }