github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/pkg/opensearch_dashboards.go (about)

     1  // Copyright (c) 2022, 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 pkg
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"encoding/json"
    10  	"fmt"
    11  	"github.com/hashicorp/go-retryablehttp"
    12  	"github.com/verrazzano/verrazzano/pkg/k8sutil"
    13  	"net/http"
    14  	"reflect"
    15  	"strings"
    16  	"text/template"
    17  
    18  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    19  )
    20  
    21  type IndexPattern struct {
    22  	Name string
    23  }
    24  
    25  // ListIndexPatterns gets the configured index patterns in OpenSearch Dashboards
    26  func ListIndexPatterns(kubeconfigPath string) []string {
    27  	list := []string{}
    28  	url := fmt.Sprintf("%s/api/saved_objects/_find?type=index-pattern&fields=title", getOpenSearchDashboardsURL(kubeconfigPath))
    29  	username, password, err := getOpenSearchDashboardsUsernamePassword(kubeconfigPath)
    30  	if err != nil {
    31  		return list
    32  	}
    33  	resp, err := getOpenSearchDashboardsWithBasicAuth(url, "", username, password, kubeconfigPath)
    34  	if err != nil {
    35  		Log(Error, fmt.Sprintf("Error getting Opensearch indices: url=%s, error=%v", url, err))
    36  		return list
    37  	}
    38  	if resp.StatusCode != http.StatusOK {
    39  		Log(Error, fmt.Sprintf("Error retrieving Opensearch indices: url=%s, status=%d", url, resp.StatusCode))
    40  		return list
    41  	}
    42  	Log(Debug, fmt.Sprintf("indices: %s", resp.Body))
    43  	var responseMap map[string]interface{}
    44  	if err := json.Unmarshal(resp.Body, &responseMap); err != nil {
    45  		Log(Error, fmt.Sprintf("OpenSearch Dashboards: Error unmarshalling index patterns response body: %v", err))
    46  	}
    47  	if responseMap["saved_objects"] != nil {
    48  		savedObjects := reflect.ValueOf(responseMap["saved_objects"])
    49  		for i := 0; i < savedObjects.Len(); i++ {
    50  			Log(Debug, fmt.Sprintf("OpenSearch Dashboards: Index pattern details: %v", savedObjects.Index(i)))
    51  			savedObject := savedObjects.Index(i).Interface().(map[string]interface{})
    52  			attributes := savedObject["attributes"].(map[string]interface{})
    53  			if attributes["title"].(string) != "" {
    54  				list = append(list, attributes["title"].(string))
    55  			}
    56  		}
    57  	}
    58  	return list
    59  }
    60  
    61  // LogIndexPatternFound confirms a named index pattern can be found in OpenSearch Dashboards in the cluster specified in the environment
    62  func LogIndexPatternFound(indexName string) bool {
    63  	kubeconfigPath, err := k8sutil.GetKubeConfigLocation()
    64  	if err != nil {
    65  		Log(Error, fmt.Sprintf("Error getting kubeconfig, error: %v", err))
    66  		return false
    67  	}
    68  
    69  	return LogIndexFoundInCluster(indexName, kubeconfigPath)
    70  }
    71  
    72  // LogIndexFoundInCluster confirms a named index pattern can be found in OpenSearch Dashboards on the given cluster
    73  func LogIndexPatternFoundInCluster(indexName, kubeconfigPath string) bool {
    74  	Log(Info, fmt.Sprintf("Looking for log index %s in cluster with kubeconfig %s", indexName, kubeconfigPath))
    75  	for _, name := range ListIndexPatterns(kubeconfigPath) {
    76  		if name == indexName {
    77  			return true
    78  		}
    79  	}
    80  	Log(Error, fmt.Sprintf("Expected to find log index %s", indexName))
    81  	return false
    82  }
    83  
    84  // CreateIndexPattern creates the specified index pattern in OpenSearch Dashboards
    85  func CreateIndexPattern(pattern string) map[string]interface{} {
    86  	template, err := template.New("indexPatternTemplate").Parse(indexPatternTemplate)
    87  	if err != nil {
    88  		Log(Error, fmt.Sprintf("Error: %v", err))
    89  	}
    90  	var buffer bytes.Buffer
    91  	err = template.Execute(&buffer, IndexPattern{Name: pattern})
    92  	if err != nil {
    93  		Log(Error, fmt.Sprintf("Error: %v", err))
    94  	}
    95  	var result map[string]interface{}
    96  	resp, err := PostOpensearchDashboards("api/saved_objects/index-pattern", buffer.String(), "osd-xsrf:true", "kbn-xsrf: true")
    97  	if err != nil {
    98  		Log(Error, fmt.Sprintf("Error creating index patterns in OpenSearchDashboards: error=%s", err))
    99  		return result
   100  	}
   101  	if resp.StatusCode != http.StatusOK {
   102  		Log(Error, fmt.Sprintf("Error creating index patterns in OpenSearchDashboards: status=%d", resp.StatusCode))
   103  		return result
   104  	}
   105  	json.Unmarshal(resp.Body, &result)
   106  	return result
   107  }
   108  
   109  // PostOpensearchDashboards POST the request entity body to Opensearch API path
   110  // The provided path is appended to the OpenSearchDashboards base URL
   111  func PostOpensearchDashboards(path string, body string, additionalHeaders ...string) (*HTTPResponse, error) {
   112  	kubeconfigPath, err := k8sutil.GetKubeConfigLocation()
   113  	if err != nil {
   114  		Log(Error, fmt.Sprintf("Error getting kubeconfig: %v", err))
   115  		return nil, err
   116  	}
   117  	url := fmt.Sprintf("%s/%s", getOpenSearchDashboardsURL(kubeconfigPath), path)
   118  	configPath, err := k8sutil.GetKubeConfigLocation()
   119  	if err != nil {
   120  		Log(Error, fmt.Sprintf("Error retrieving kubeconfig, error=%v", err))
   121  		return nil, err
   122  	}
   123  	username, password, err := getOpenSearchDashboardsUsernamePassword(configPath)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	Log(Debug, fmt.Sprintf("REST API path: %v \nQuery: \n%v", url, body))
   128  	resp, err := postOpenSearchDashboardsWithBasicAuth(url, body, username, password, configPath, additionalHeaders...)
   129  	return resp, err
   130  }
   131  
   132  // getOpenSearchDashboardsURL gets the OpenSearch Dashboards Ingress host in the given cluster
   133  func getOpenSearchDashboardsURL(kubeconfigPath string) string {
   134  	clientset, err := GetKubernetesClientsetForCluster(kubeconfigPath)
   135  	isMinversion150, _ := IsVerrazzanoMinVersion("1.5.0", kubeconfigPath)
   136  	isMinversion170, _ := IsVerrazzanoMinVersion("1.7.0", kubeconfigPath)
   137  
   138  	if err != nil {
   139  		Log(Error, fmt.Sprintf("Failed to get clientset for cluster %v", err))
   140  		return ""
   141  	}
   142  	ingressList, _ := clientset.NetworkingV1().Ingresses("verrazzano-system").List(context.TODO(), metav1.ListOptions{})
   143  	for _, ingress := range ingressList.Items {
   144  		if (isMinversion170 && ingress.Name == "opensearch-dashboards") || (isMinversion150 && ingress.Name == "vmi-system-osd") || (!isMinversion150 && ingress.Name == "vmi-system-kibana") {
   145  			Log(Info, fmt.Sprintf("Found Kibana/OpenSearch Dashboards Ingress %v, host %s", ingress.Name, ingress.Spec.Rules[0].Host))
   146  			return fmt.Sprintf("https://%s", ingress.Spec.Rules[0].Host)
   147  		}
   148  	}
   149  	return ""
   150  }
   151  
   152  // getOpenSearchDashboardsUsernamePassword gets the Verrazzano user name and password
   153  func getOpenSearchDashboardsUsernamePassword(kubeconfigPath string) (username, password string, err error) {
   154  	password, err = GetVerrazzanoPasswordInCluster(kubeconfigPath)
   155  	if err != nil {
   156  		return "", "", err
   157  	}
   158  	return "verrazzano", password, err
   159  }
   160  
   161  // getOpenSearchDashboardsWithBasicAuth access OpenSearch Dashboards with GET using basic auth, using a given kubeconfig
   162  func getOpenSearchDashboardsWithBasicAuth(url string, hostHeader string, username string, password string, kubeconfigPath string) (*HTTPResponse, error) {
   163  	retryableClient, err := getOpenSearchDashboardsClient(kubeconfigPath)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  	return doReq(url, "GET", "", hostHeader, username, password, nil, retryableClient)
   168  }
   169  
   170  // postOpenSearchDashboardsWithBasicAuth retries POST to OpenSearch Dashboards using basic auth
   171  func postOpenSearchDashboardsWithBasicAuth(url, body, username, password, kubeconfigPath string, additionalHeaders ...string) (*HTTPResponse, error) {
   172  	retryableClient, err := getOpenSearchDashboardsClient(kubeconfigPath)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	return doReq(url, "POST", "application/json", "", username, password, strings.NewReader(body), retryableClient, additionalHeaders...)
   177  }
   178  
   179  func getOpenSearchDashboardsClient(kubeconfigPath string) (*retryablehttp.Client, error) {
   180  	var retryableClient *retryablehttp.Client
   181  	var err error
   182  	retryableClient, err = GetVerrazzanoHTTPClient(kubeconfigPath)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  	return retryableClient, nil
   187  }
   188  
   189  const indexPatternTemplate = `{
   190        "attributes": {
   191          "title": "{{.Name}}"
   192        }
   193      }
   194  `