github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/pkg/vmi/elastic.go (about) 1 // Copyright (c) 2020, 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 vmi 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "net/http" 10 11 "github.com/hashicorp/go-retryablehttp" 12 "github.com/verrazzano/verrazzano/pkg/httputil" 13 "github.com/verrazzano/verrazzano/pkg/k8sutil" 14 "github.com/verrazzano/verrazzano/tests/e2e/pkg" 15 ) 16 17 // Opensearch contains information about the Opensearch instance 18 type Opensearch struct { 19 ClusterName string `json:"cluster_name"` 20 EsVersion OpensearchVersion `json:"version"` 21 binding string 22 vmiHTTPClient *retryablehttp.Client 23 OperatorManaged bool 24 opensearchIngress string 25 osdIngress string 26 } 27 28 // OpensearchVersion contains information about the version of Opensearch instance 29 type OpensearchVersion struct { 30 Number string `json:"number"` 31 Distribution string `json:"distribution"` 32 } 33 34 // GetOpensearch gets Opensearch representing the opensearch cluster with the binding name 35 func GetOpensearch(binding string, operatorManaged bool) *Opensearch { 36 opensearchIngress := fmt.Sprintf("vmi-%v-os-ingest", binding) 37 osdIngress := fmt.Sprintf("vmi-%v-osd", binding) 38 if operatorManaged { 39 opensearchIngress = "opensearch" 40 osdIngress = "opensearch-dashboards" 41 } 42 return &Opensearch{ 43 binding: binding, 44 OperatorManaged: operatorManaged, 45 opensearchIngress: opensearchIngress, 46 osdIngress: osdIngress, 47 } 48 } 49 50 // PodsRunning checks if all opensearch required pods are running 51 func (e *Opensearch) PodsRunning() bool { 52 expectedOpensearchPods := []string{ 53 fmt.Sprintf("vmi-%s-es-master", e.binding), 54 fmt.Sprintf("vmi-%s-kibana", e.binding), 55 fmt.Sprintf("vmi-%s-grafana", e.binding), 56 fmt.Sprintf("vmi-%s-prometheus", e.binding), 57 fmt.Sprintf("vmi-%s-api", e.binding)} 58 running, _ := pkg.PodsRunning("verrazzano-system", expectedOpensearchPods) 59 60 if running { 61 expectedOpensearchPods = []string{ 62 fmt.Sprintf("vmi-%s-es-ingest", e.binding), 63 fmt.Sprintf("vmi-%s-es-data", e.binding)} 64 running, _ = pkg.PodsRunning("verrazzano-system", expectedOpensearchPods) 65 } 66 67 return running 68 } 69 70 // getResponseBody gets the response body for the specified path from opensearch cluster 71 func (e *Opensearch) getResponseBody(path string) ([]byte, error) { 72 kubeConfigPath, err := k8sutil.GetKubeConfigLocation() 73 if err != nil { 74 pkg.Log(pkg.Error, fmt.Sprintf("Error getting kubeconfig: %v", err)) 75 return nil, err 76 } 77 78 api := pkg.EventuallyGetAPIEndpoint(kubeConfigPath) 79 esURL, err := api.GetOpensearchURL() 80 if err != nil { 81 pkg.Log(pkg.Error, fmt.Sprintf("Error getting Opensearch URL: %v", err)) 82 return nil, err 83 } 84 85 esURL = esURL + path 86 87 password, err := pkg.GetVerrazzanoPasswordInCluster(kubeConfigPath) 88 if err != nil { 89 return nil, err 90 } 91 92 return e.retryGet(esURL, pkg.Username, password, kubeConfigPath) 93 } 94 95 // Connect checks if the Opensearch cluster can be connected 96 func (e *Opensearch) Connect() bool { 97 body, err := e.getResponseBody("/") 98 if err != nil { 99 return false 100 } 101 err = json.Unmarshal(body, e) 102 return err == nil 103 } 104 105 func (e *Opensearch) retryGet(url, username, password string, kubeconfigPath string) ([]byte, error) { 106 req, _ := retryablehttp.NewRequest("GET", url, nil) 107 req.SetBasicAuth(username, password) 108 client, err := e.GetVmiHTTPClient(kubeconfigPath) 109 if err != nil { 110 pkg.Log(pkg.Info, fmt.Sprintf("Error getting HTTP client: %v", err)) 111 return nil, err 112 } 113 client.CheckRetry = pkg.GetRetryPolicy() 114 resp, err := client.Do(req) 115 if err != nil { 116 pkg.Log(pkg.Info, fmt.Sprintf("Error GET %v error: %v", url, err)) 117 return nil, err 118 } 119 if resp.StatusCode != 200 { 120 pkg.Log(pkg.Info, fmt.Sprintf("Response status code: %d", resp.StatusCode)) 121 } 122 httpResp, err := pkg.ProcessHTTPResponse(resp) 123 if err != nil { 124 pkg.Log(pkg.Info, fmt.Sprintf("Error reading response from GET %v error: %v", url, err)) 125 return nil, err 126 } 127 if httpResp.StatusCode == http.StatusNotFound { 128 err = fmt.Errorf("url %s returned not found", url) 129 pkg.Log(pkg.Info, fmt.Sprintf("NotFound %v error: %v", url, err)) 130 return nil, err 131 } 132 return httpResp.Body, nil 133 } 134 135 func (e *Opensearch) GetOSIngressName() string { 136 return e.opensearchIngress 137 } 138 139 func (e *Opensearch) GetOSDIngressName() string { 140 return e.osdIngress 141 } 142 143 func (e *Opensearch) GetVmiHTTPClient(kubeconfigPath string) (*retryablehttp.Client, error) { 144 if e.vmiHTTPClient == nil { 145 var err error 146 e.vmiHTTPClient, err = pkg.GetVerrazzanoHTTPClient(kubeconfigPath) 147 if err != nil { 148 return nil, err 149 } 150 } 151 return e.vmiHTTPClient, nil 152 } 153 154 // ListIndices lists Opensearch indices 155 func (e *Opensearch) ListIndices() []string { 156 idx := []string{} 157 for i := range e.getIndices() { 158 idx = append(idx, i) 159 } 160 return idx 161 } 162 163 // getIndices gets index metadata (aliases, mappings, and settings) of all Opensearch indices in the given cluster 164 func (e *Opensearch) getIndices() map[string]interface{} { 165 body, err := e.getResponseBody("/_all") 166 if err != nil { 167 pkg.Log(pkg.Info, fmt.Sprintf("Error ListIndices error: %v", err)) 168 return nil 169 } 170 var indices map[string]interface{} 171 json.Unmarshal(body, &indices) 172 return indices 173 } 174 175 // CheckTLSSecret checks the Opensearch secret 176 func (e *Opensearch) CheckTLSSecret() bool { 177 secretName := fmt.Sprintf("%v-tls", e.binding) 178 return pkg.SecretsCreated("verrazzano-system", secretName) 179 } 180 181 // CheckHealth checks the health status of Opensearch cluster 182 // Returns true if the health status is green otherwise false 183 func (e *Opensearch) CheckHealth(kubeconfigPath string) bool { 184 supported, err := pkg.IsVerrazzanoMinVersion("1.1.0", kubeconfigPath) 185 if err != nil { 186 pkg.Log(pkg.Error, fmt.Sprintf("Error getting Verrazzano version: %v", err)) 187 return false 188 } 189 if !supported { 190 pkg.Log(pkg.Info, "Skipping Elasticsearch cluster health check since version < 1.1.0") 191 return true 192 } 193 body, err := e.getResponseBody("/_cluster/health") 194 if err != nil { 195 pkg.Log(pkg.Error, fmt.Sprintf("Error getting cluster health: %v", err)) 196 return false 197 } 198 pkg.Log(pkg.Info, fmt.Sprintf("Response body %v", string(body))) 199 status, err := httputil.ExtractFieldFromResponseBodyOrReturnError(string(body), "status", "unable to find status in Opensearch health response") 200 if err != nil { 201 pkg.Log(pkg.Error, fmt.Sprintf("Error extracting health status from response body: %v", err)) 202 return false 203 } 204 indexTemplate, _ := e.getResponseBody("/_index_template") 205 pkg.Log(pkg.Info, fmt.Sprintf("IndexTemplate: %v", string(indexTemplate))) 206 catIndices, _ := e.getResponseBody("/_cat/indices") 207 pkg.Log(pkg.Info, fmt.Sprintf("Indices: \n%v", string(catIndices))) 208 if status == "green" { 209 pkg.Log(pkg.Info, "Opensearch cluster health status is green") 210 return true 211 } else if status == "yellow" { // Temporary WA for security audit log index as it gets created with 1 replica 212 pkg.Log(pkg.Info, "Opensearch cluster health status is yellow") 213 return true 214 } 215 pkg.Log(pkg.Error, fmt.Sprintf("Opensearch cluster health status is %v instead of green", status)) 216 return false 217 } 218 219 // CheckIndicesHealth checks the health status of indices in a cluster 220 // Returns true if the health status of all the indices is green otherwise false 221 func (e *Opensearch) CheckIndicesHealth(kubeconfigPath string) bool { 222 supported, err := pkg.IsVerrazzanoMinVersion("1.1.0", kubeconfigPath) 223 if err != nil { 224 pkg.Log(pkg.Error, fmt.Sprintf("Error getting Verrazzano version: %v", err)) 225 return false 226 } 227 if !supported { 228 pkg.Log(pkg.Info, "Skipping Elasticsearch indices health check since version < 1.1.0") 229 return true 230 } 231 body, err := e.getResponseBody("/_cat/indices?format=json") 232 if err != nil { 233 pkg.Log(pkg.Error, fmt.Sprintf("Error getting cluster indices: %v", err)) 234 return false 235 } 236 pkg.Log(pkg.Info, fmt.Sprintf("Response body %v", string(body))) 237 var indices []map[string]interface{} 238 if err := json.Unmarshal(body, &indices); err != nil { 239 pkg.Log(pkg.Error, fmt.Sprintf("Error unmarshalling indices response body: %v", err)) 240 return false 241 } 242 243 for _, index := range indices { 244 pkg.Log(pkg.Debug, fmt.Sprintf("Index details: %v", index)) 245 val, found := index["health"] 246 if !found { 247 pkg.Log(pkg.Error, fmt.Sprintf("Not able to find the health of the index: %v", index)) 248 return false 249 } 250 // Temporary WA for security audit log index as it gets created with 1 replica 251 if val.(string) == "red" { 252 pkg.Log(pkg.Error, fmt.Sprintf("Current index health status %v is not green or yellow", val)) 253 return false 254 } 255 } 256 pkg.Log(pkg.Info, "The health status of all the indices is green or yellow") 257 return true 258 } 259 260 // //Check the Opensearch certificate 261 // func (e *Opensearch) CheckCertificate() bool { 262 // certList, _ := pkg.ListCertificates("verrazzano-system") 263 // for _, cert := range certList.Items { 264 // if cert.Name == fmt.Sprintf("%v-tls", e.binding) { 265 // pkg.Log(pkg.Info, fmt.Sprintf("Found Certificate %v for binding %v", cert.Name, e.binding)) 266 // for _, condition := range cert.Status.Conditions { 267 // if condition.Type == "Ready" { 268 // pkg.Log(pkg.Info, fmt.Sprintf("Certificate %v status: Ready = %v", cert.Name, condition.Status)) 269 // return condition.Status == "True" 270 // } 271 // } 272 // } 273 // } 274 // return false 275 // } 276 277 // CheckIngress checks the Opensearch Ingress 278 func (e *Opensearch) CheckIngress() bool { 279 ingressList, err := pkg.ListIngresses("verrazzano-system") 280 if err != nil { 281 pkg.Log(pkg.Error, fmt.Sprintf("Could not get list of ingresses: %v", err)) 282 return false 283 } 284 for _, ingress := range ingressList.Items { 285 if ingress.Name == e.opensearchIngress { 286 pkg.Log(pkg.Info, fmt.Sprintf("Found Ingress %v for binding %v", ingress.Name, e.binding)) 287 return true 288 } 289 } 290 return false 291 }