github.com/verrazzano/verrazzano-monitoring-operator@v0.0.30/pkg/opensearch_dashboards/opensearch_dashboards_upgrade.go (about) 1 // Copyright (C) 2022, 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 dashboards 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/config" 10 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/resources" 11 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/util/logs/vzlog" 12 "io/ioutil" 13 "net/http" 14 "regexp" 15 "strings" 16 ) 17 18 const updatePatternPayload = `{"attributes":{"title":"%s"}}` 19 20 type ( 21 IndexPatterns struct { 22 Total int `json:"total"` 23 Page int `json:"page"` 24 SavedObjects []SavedObject `json:"saved_objects,omitempty"` 25 } 26 27 SavedObject struct { 28 ID string `json:"id"` 29 Attributes `json:"attributes"` 30 } 31 32 Attributes struct { 33 Title string `json:"title"` 34 } 35 ) 36 37 func (od *OSDashboardsClient) updatePatternsInternal(log vzlog.VerrazzanoLogger, dashboardsEndPoint string) error { 38 // Get index patterns configured in OpenSearch Dashboards 39 savedObjects, err := od.getPatterns(dashboardsEndPoint, 100) 40 if err != nil { 41 return err 42 } 43 for _, savedObject := range savedObjects { 44 updatedPattern := constructUpdatedPattern(savedObject.Title) 45 if updatedPattern == "" || (savedObject.Title == updatedPattern) { 46 continue 47 } 48 // Invoke update index pattern API 49 err = od.executeUpdate(log, dashboardsEndPoint, savedObject.ID, savedObject.Title, updatedPattern) 50 if err != nil { 51 return fmt.Errorf("failed to updated index pattern %s: %v", savedObject.Title, err) 52 } 53 } 54 return nil 55 } 56 57 func (od *OSDashboardsClient) getPatterns(dashboardsEndPoint string, perPage int) ([]SavedObject, error) { 58 var savedObjects []SavedObject 59 currentPage := 1 60 61 // Index Pattern is a paginated response type, so we need to page out all data 62 for { 63 url := fmt.Sprintf("%s/api/saved_objects/_find?type=index-pattern&fields=title&per_page=%d&page=%d", dashboardsEndPoint, perPage, currentPage) 64 req, err := http.NewRequest("GET", url, nil) 65 if err != nil { 66 return nil, err 67 } 68 resp, err := od.DoHTTP(req) 69 if err != nil { 70 return nil, err 71 } 72 defer resp.Body.Close() 73 if resp.StatusCode != http.StatusOK { 74 return nil, fmt.Errorf("got code %d when querying index patterns", resp.StatusCode) 75 } 76 indexPatterns := &IndexPatterns{} 77 if err := json.NewDecoder(resp.Body).Decode(indexPatterns); err != nil { 78 return nil, fmt.Errorf("failed to decode index pattern response body: %v", err) 79 } 80 currentPage++ 81 savedObjects = append(savedObjects, indexPatterns.SavedObjects...) 82 // paginate responses until we have all the index patterns 83 if len(savedObjects) >= indexPatterns.Total { 84 break 85 } 86 } 87 88 return savedObjects, nil 89 } 90 91 func (od *OSDashboardsClient) executeUpdate(log vzlog.VerrazzanoLogger, dashboardsEndPoint string, 92 id string, originalPattern string, updatedPattern string) error { 93 payload := createIndexPatternPayload(updatedPattern) 94 log.Infof("Replacing index pattern %s with %s in OpenSearch Dashboards", originalPattern, updatedPattern) 95 updatedPatternURL := fmt.Sprintf("%s/api/saved_objects/index-pattern/%s", dashboardsEndPoint, id) 96 log.Debugf("Executing update saved object API %s", updatedPatternURL) 97 req, err := http.NewRequest("PUT", updatedPatternURL, strings.NewReader(payload)) 98 if err != nil { 99 return err 100 } 101 req.Header.Add("Content-Type", "application/json") 102 req.Header.Add("osd-xsrf", "true") 103 resp, err := od.DoHTTP(req) 104 if err != nil { 105 return fmt.Errorf("failed to get index patterns from OpenSearch dashboards: %v", err) 106 } 107 if resp.StatusCode != http.StatusOK { 108 return fmt.Errorf("got status code %d when getting index patterns", resp.StatusCode) 109 } 110 111 responseBody, _ := ioutil.ReadAll(resp.Body) 112 log.Debugf("Response from OpenSearch Dashboards update index API: %s", responseBody) 113 return nil 114 } 115 116 func createIndexPatternPayload(indexPattern string) string { 117 return fmt.Sprintf(updatePatternPayload, indexPattern) 118 } 119 120 /* 121 * constructUpdatedPattern constructs the updated pattern as follows: 122 * - Update index patterns matching old system indices to match data stream verrazzano-system 123 * - Update index patterns matching old application indices verrazzano-namespace-<application namespace> 124 * to match data stream verrazzano-application-<application namespace> 125 */ 126 func constructUpdatedPattern(originalPattern string) string { 127 var updatedPattern []string 128 patternList := strings.Split(originalPattern, ",") 129 for _, eachPattern := range patternList { 130 if strings.HasPrefix(eachPattern, "verrazzano-") && eachPattern != "verrazzano-*" { 131 // To match the exact pattern, add ^ in the beginning and $ in the end 132 regexpString := resources.ConvertToRegexp(eachPattern) 133 systemIndexMatch := isSystemIndexMatch(regexpString) 134 if systemIndexMatch { 135 updatedPattern = append(updatedPattern, config.DataStreamName()) 136 } 137 isNamespaceIndexMatch, _ := regexp.MatchString(regexpString, "verrazzano-namespace-") 138 if isNamespaceIndexMatch { 139 updatedPattern = append(updatedPattern, "verrazzano-application-*") 140 } else if strings.HasPrefix(eachPattern, "verrazzano-namespace-") { 141 // If the pattern matches system index and no * present in the pattern, then it is considered as only 142 // system index 143 if systemIndexMatch && !strings.Contains(eachPattern, "*") { 144 continue 145 } 146 updatedPattern = append(updatedPattern, strings.Replace(eachPattern, "verrazzano-namespace-", "verrazzano-application-", 1)) 147 } 148 } else { 149 updatedPattern = append(updatedPattern, eachPattern) 150 } 151 } 152 return strings.Join(updatedPattern, ",") 153 } 154 155 func isSystemIndexMatch(pattern string) bool { 156 logStashIndex, _ := regexp.MatchString(pattern, "verrazzano-logstash-") 157 systemJournalIndex, _ := regexp.MatchString(pattern, "verrazzano-systemd-journal") 158 if logStashIndex || systemJournalIndex { 159 return true 160 } 161 for _, namespace := range config.SystemNamespaces() { 162 systemIndex, _ := regexp.MatchString(pattern, "verrazzano-namespace-"+namespace) 163 if systemIndex { 164 return true 165 } 166 } 167 return false 168 }