github.com/verrazzano/verrazzano-monitoring-operator@v0.0.30/pkg/opensearch/opensearch_upgrade_test.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 opensearch
     5  
     6  import (
     7  	"errors"
     8  	"io"
     9  	"net/http"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	vmcontrollerv1 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/apis/vmcontroller/v1"
    15  	"github.com/verrazzano/verrazzano-monitoring-operator/pkg/config"
    16  	"github.com/verrazzano/verrazzano-monitoring-operator/pkg/util/logs/vzlog"
    17  )
    18  
    19  const fakeIndicesResponse = `{
    20  	"verrazzano-namespace-dummyapp": {
    21  		"aliases": {}
    22  	},
    23  	"verrazzano-namespace-testapp": {
    24  		"aliases": {}
    25  	},
    26      "verrazzano-namespace-metallb-system": {
    27          "aliases": {}
    28      },
    29      "verrazzano-namespace-verrazzano-system": {
    30          "aliases": {}
    31      },
    32      "verrazzano-systemd-journal": {
    33          "aliases": {}
    34      },
    35      "verrazzano-namespace-keycloak": {
    36          "aliases": {}
    37      },
    38      "verrazzano-namespace-local-path-storage": {
    39          "aliases": {}
    40      },
    41      "verrazzano-namespace-istio-system": {
    42          "aliases": {}
    43      },
    44      "verrazzano-namespace-ingress-nginx": {
    45          "aliases": {}
    46      },
    47      "verrazzano-namespace-cert-manager": {
    48          "aliases": {}
    49      },
    50      "verrazzano-namespace-kube-system": {
    51          "aliases": {}
    52      },
    53      "verrazzano-namespace-monitoring": {
    54          "aliases": {}
    55      },
    56      ".kibana_1": {
    57          "aliases": {
    58              ".kibana": {}
    59          }
    60      }
    61  }`
    62  
    63  const openSearchEP = "http://localhost:9200/"
    64  
    65  // TestGetIndices tests that indices can be fetched from the OpenSearch server
    66  // GIVEN an OpenSearch server with indices
    67  // WHEN I call getIndices, getSystemIndices, and getApplicationIndices
    68  // THEN I get back the expected indices, system indices, and application indices
    69  func TestGetIndices(t *testing.T) {
    70  	o := NewOSClient(statefulSetLister)
    71  	o.DoHTTP = func(request *http.Request) (*http.Response, error) {
    72  		return &http.Response{
    73  			StatusCode: http.StatusOK,
    74  			Body:       io.NopCloser(strings.NewReader(fakeIndicesResponse)),
    75  		}, nil
    76  	}
    77  	log := vzlog.DefaultLogger()
    78  	indices, err := o.getIndices(log, openSearchEP)
    79  	assert.NoError(t, err)
    80  	assert.Contains(t, indices, "verrazzano-namespace-keycloak")
    81  	assert.Contains(t, indices, "verrazzano-namespace-istio-system")
    82  	assert.Equal(t, 13, len(indices))
    83  
    84  	systemIndices := getSystemIndices(log, indices)
    85  	assert.Contains(t, systemIndices, "verrazzano-namespace-keycloak")
    86  	assert.Contains(t, systemIndices, "verrazzano-namespace-istio-system")
    87  	assert.Equal(t, 10, len(systemIndices))
    88  
    89  	applicationIndices := getApplicationIndices(log, indices)
    90  	assert.Contains(t, applicationIndices, "verrazzano-namespace-testapp")
    91  	assert.Contains(t, applicationIndices, "verrazzano-namespace-dummyapp")
    92  	assert.Equal(t, 2, len(applicationIndices))
    93  }
    94  
    95  // TestDataStreamExists Tests the expected data streams can be retrieved on an OpenSearch cluster
    96  // GIVEN a cluster with data streams on it
    97  // WHEN I call DataStreamExists
    98  // THEN true is returned if the data stream is present
    99  func TestDataStreamExists(t *testing.T) {
   100  	a := assert.New(t)
   101  	o := NewOSClient(statefulSetLister)
   102  	o.DoHTTP = func(request *http.Request) (*http.Response, error) {
   103  		if strings.Contains(request.URL.Path, config.DataStreamName()) {
   104  			return &http.Response{
   105  				StatusCode: http.StatusOK,
   106  				Body:       io.NopCloser(strings.NewReader("")),
   107  			}, nil
   108  		}
   109  		return &http.Response{
   110  			StatusCode: http.StatusNotFound,
   111  			Body:       io.NopCloser(strings.NewReader("")),
   112  		}, nil
   113  	}
   114  	exists, err := o.DataStreamExists(openSearchEP, config.DataStreamName())
   115  	a.NoError(err)
   116  	a.True(exists)
   117  	exists, err = o.DataStreamExists(openSearchEP, "unknown")
   118  	a.NoError(err)
   119  	a.False(exists)
   120  }
   121  
   122  // TestCalculateSeconds Tests formatting of OpenSearch time units to seconds
   123  // GIVEN an aribtrary time unit string
   124  // WHEN I call calculateSeconds
   125  // THEN the number of seconds that the time unit string represents is returned
   126  func TestCalculateSeconds(t *testing.T) {
   127  	a := assert.New(t)
   128  	_, err := calculateSeconds("ww5s")
   129  	a.Error(err, "Error should be returned from exec")
   130  	_, err = calculateSeconds("12y")
   131  	a.Error(err, "should fail for 'months'")
   132  	_, err = calculateSeconds("10M")
   133  	a.Error(err, "should fail for 'months'")
   134  	seconds, err := calculateSeconds("6d")
   135  	a.NoError(err, "Should not fail for valid day unit")
   136  	a.Equal(uint64(518400), seconds)
   137  	seconds, err = calculateSeconds("120m")
   138  	a.NoError(err, "Should not fail for valid minute unit")
   139  	a.Equal(uint64(7200), seconds)
   140  	seconds, err = calculateSeconds("5h")
   141  	a.NoError(err, "Should not fail for valid hour unit")
   142  	a.Equal(uint64(18000), seconds)
   143  	seconds, err = calculateSeconds("20s")
   144  	a.NoError(err, "Should not fail for valid second unit")
   145  	a.Equal(uint64(20), seconds)
   146  }
   147  
   148  // TestReindexAndDeleteIndices Tests that indices are reindexed and deleted as expected
   149  // GIVEN a cluster with indices to reindex
   150  // WHEN I call reindexAndDeleteIndices
   151  // THEN those indices are reindexed and deleted
   152  func TestReindexAndDeleteIndices(t *testing.T) {
   153  	systemIndices := []string{"verrazzano-namespace-verrazzano-system"}
   154  	dummyOK := func(req *http.Request) (*http.Response, error) {
   155  		return &http.Response{
   156  			StatusCode: http.StatusOK,
   157  			Body:       io.NopCloser(strings.NewReader("")),
   158  		}, nil
   159  	}
   160  	var tests = []struct {
   161  		name          string
   162  		indices       []string
   163  		httpFunc      func(req *http.Request) (*http.Response, error)
   164  		isSystemIndex bool
   165  		isError       bool
   166  	}{
   167  		{
   168  			"should reindex system indices",
   169  			systemIndices,
   170  			dummyOK,
   171  			true,
   172  			false,
   173  		},
   174  		{
   175  			"should reindex application indices",
   176  			[]string{"verrazzano-namespace-bobs-books", "verrazzano-namespace-todo-app"},
   177  			dummyOK,
   178  			false,
   179  			false,
   180  		},
   181  		{
   182  			"should fail when reindex fails",
   183  			systemIndices,
   184  			func(req *http.Request) (*http.Response, error) {
   185  				return nil, errors.New("BOOM")
   186  			},
   187  			true,
   188  			true,
   189  		},
   190  		{
   191  			"should fail when delete not found",
   192  			systemIndices,
   193  			func(req *http.Request) (*http.Response, error) {
   194  				if req.Method == "POST" {
   195  					return dummyOK(req)
   196  				}
   197  				return &http.Response{
   198  					StatusCode: http.StatusNotFound,
   199  					Body:       io.NopCloser(strings.NewReader("")),
   200  				}, nil
   201  			},
   202  			true,
   203  			true,
   204  		},
   205  		{
   206  			"should fail when delete fails",
   207  			systemIndices,
   208  			func(req *http.Request) (*http.Response, error) {
   209  				if req.Method == "POST" {
   210  					return dummyOK(req)
   211  				}
   212  				return nil, errors.New("BOOM")
   213  			},
   214  			true,
   215  			true,
   216  		},
   217  		{
   218  			"should fail when reindex 5xx",
   219  			systemIndices,
   220  			func(req *http.Request) (*http.Response, error) {
   221  				return &http.Response{
   222  					StatusCode: http.StatusInternalServerError,
   223  					Body:       io.NopCloser(strings.NewReader("")),
   224  				}, nil
   225  			},
   226  			true,
   227  			true,
   228  		},
   229  	}
   230  	vmi := createISMVMI("1d", true)
   231  	vmi.Spec.Elasticsearch.Policies = []vmcontrollerv1.IndexManagementPolicy{
   232  		*createTestPolicy("1d", "1d", "verrazzano-*", "1d", 1),
   233  	}
   234  	for _, tt := range tests {
   235  		t.Run(tt.name, func(t *testing.T) {
   236  			o := NewOSClient(statefulSetLister)
   237  			o.DoHTTP = tt.httpFunc
   238  			err := o.reindexAndDeleteIndices(vzlog.DefaultLogger(), vmi, openSearchEP, tt.indices, tt.isSystemIndex)
   239  			if tt.isError {
   240  				assert.Error(t, err)
   241  			} else {
   242  				assert.NoError(t, err)
   243  			}
   244  		})
   245  	}
   246  }
   247  
   248  // TestReindexToDataStream Tests reindexing an index to a data stream
   249  // GIVENa cluster with an index to reindex
   250  // WHEN I call reindexToDataStream
   251  // THEN the index is reindex to a data stream
   252  func TestReindexToDataStream(t *testing.T) {
   253  	o := NewOSClient(statefulSetLister)
   254  	o.DoHTTP = func(request *http.Request) (*http.Response, error) {
   255  		return &http.Response{
   256  			StatusCode: http.StatusOK,
   257  			Body:       io.NopCloser(strings.NewReader("")),
   258  		}, nil
   259  	}
   260  	err := o.reindexToDataStream(vzlog.DefaultLogger(), openSearchEP, "src", "dest", "1s")
   261  	assert.NoError(t, err)
   262  }