github.com/minio/console@v1.4.1/replication/admin_api_int_replication_test.go (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2022 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // These tests are for AdminAPI Tag based on swagger-console.yml
    18  
    19  package replication
    20  
    21  import (
    22  	"bytes"
    23  	"encoding/json"
    24  	"fmt"
    25  	"io"
    26  	"log"
    27  	"net/http"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/minio/console/models"
    32  	"github.com/stretchr/testify/assert"
    33  )
    34  
    35  const apiURL = "http://localhost:9090/api/v1/admin/site-replication"
    36  
    37  func makeExecuteReq(method string, body io.Reader) (*http.Response, error) {
    38  	client := &http.Client{
    39  		Timeout: 10 * time.Second,
    40  	}
    41  
    42  	request, err := http.NewRequest(
    43  		method,
    44  		apiURL,
    45  		body,
    46  	)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
    51  	request.Header.Add("Content-Type", "application/json")
    52  	response, err := client.Do(request)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	return response, nil
    58  }
    59  
    60  func AddSiteReplicationInfo(sites []map[string]interface{}) (int, error) {
    61  	requestDataJSON, _ := json.Marshal(sites)
    62  	requestDataBody := bytes.NewReader(requestDataJSON)
    63  	response, err := makeExecuteReq("POST", requestDataBody)
    64  	if err != nil {
    65  		log.Println(response)
    66  		return -1, err
    67  	}
    68  	if response != nil {
    69  		return response.StatusCode, nil
    70  	}
    71  
    72  	return -1, nil
    73  }
    74  
    75  func EditSiteReplicationInfo(editedSite map[string]interface{}) (int, error) {
    76  	requestDataJSON, _ := json.Marshal(editedSite)
    77  	requestDataBody := bytes.NewReader(requestDataJSON)
    78  	response, err := makeExecuteReq("PUT", requestDataBody)
    79  	if err != nil {
    80  		return -1, err
    81  	}
    82  	if response != nil {
    83  		return response.StatusCode, nil
    84  	}
    85  
    86  	return -1, nil
    87  }
    88  
    89  func DeleteSiteReplicationInfo(delReq map[string]interface{}) (int, error) {
    90  	requestDataJSON, _ := json.Marshal(delReq)
    91  	requestDataBody := bytes.NewReader(requestDataJSON)
    92  	response, err := makeExecuteReq("DELETE", requestDataBody)
    93  	if err != nil {
    94  		return -1, err
    95  	}
    96  	if response != nil {
    97  		return response.StatusCode, nil
    98  	}
    99  
   100  	return -1, nil
   101  }
   102  
   103  func TestGetSiteReplicationInfo(t *testing.T) {
   104  	assert := assert.New(t)
   105  	response, err := makeExecuteReq("GET", nil)
   106  
   107  	// defer response.Body.Close()
   108  
   109  	tgt := &models.SiteReplicationInfoResponse{}
   110  	json.NewDecoder(response.Body).Decode(tgt)
   111  	if err != nil {
   112  		log.Println(err)
   113  		return
   114  	}
   115  	if response != nil {
   116  		assert.Equal(200, response.StatusCode, "Status Code is incorrect")
   117  	}
   118  }
   119  
   120  func TestAddSiteReplicationInfo(t *testing.T) {
   121  	assert := assert.New(t)
   122  	fmt.Println("Add Site Replication")
   123  
   124  	sites := make([]map[string]interface{}, 2)
   125  	sites[0] = map[string]interface{}{
   126  		"accessKey": "minioadmin",
   127  		"endpoint":  "http://localhost:9000",
   128  		"secretKey": "minioadmin",
   129  		"name":      "sitellhost9000",
   130  	}
   131  	sites[1] = map[string]interface{}{
   132  		"accessKey": "minioadmin",
   133  		"endpoint":  "http://minio1:9001", // Docker container .
   134  		// "endpoint":  "http://localhost:9001", for local development
   135  		"secretKey": "minioadmin",
   136  		"name":      "sitellhost9001",
   137  	}
   138  
   139  	errorSites1 := make([]map[string]interface{}, 2)
   140  	errorSites1[0] = map[string]interface{}{
   141  		"accessKey": "minioadmin",
   142  		"endpoint":  "http://localhost:9000",
   143  		"secretKey": "minioadmin",
   144  		"name":      "sitellhost9000",
   145  	}
   146  	errorSites1[1] = map[string]interface{}{
   147  		"accessKey": "minioadmin",
   148  		"endpoint":  "http://non-existent:9001",
   149  		"secretKey": "minioadmin",
   150  		"name":      "sitellhost9001",
   151  	}
   152  
   153  	errorSites2 := make([]map[string]interface{}, 2)
   154  	errorSites2[0] = map[string]interface{}{
   155  		"accessKey": "minioadmin",
   156  		"endpoint":  "http://localhost:9000",
   157  		"secretKey": "minioadmin",
   158  		"name":      "sitellhost9000",
   159  	}
   160  	errorSites2[1] = map[string]interface{}{
   161  		"accessKey": "minioadmin",
   162  		"endpoint":  "http://localhost:9000",
   163  		"secretKey": "minioadmin",
   164  		"name":      "sitellhost9000",
   165  	}
   166  	type args struct {
   167  		sites []map[string]interface{}
   168  	}
   169  	tests := []struct {
   170  		name          string
   171  		args          args
   172  		expStatusCode int
   173  		expectedError bool
   174  	}{
   175  		// valid sites for replication config
   176  		{
   177  			name: "Add Two sites for Replication",
   178  			args: args{
   179  				sites: sites,
   180  			},
   181  			expStatusCode: 200,
   182  			expectedError: false,
   183  		},
   184  
   185  		// context deadline error
   186  		{
   187  			name: "Add Invalid Site name for Replication",
   188  			args: args{
   189  				sites: errorSites1,
   190  			},
   191  			expStatusCode: 500,
   192  			expectedError: true,
   193  		},
   194  
   195  		// site already added  error 500
   196  		{
   197  			name: "Add same Site name for Replication",
   198  			args: args{
   199  				sites: errorSites2,
   200  			},
   201  			expStatusCode: 500,
   202  			expectedError: true,
   203  		},
   204  	}
   205  
   206  	for _, tt := range tests {
   207  		t.Run(tt.name, func(_ *testing.T) {
   208  			actualStatusCode, err := AddSiteReplicationInfo(tt.args.sites)
   209  			fmt.Println("TestAddSiteReplicationInfo: ", actualStatusCode)
   210  
   211  			if tt.expStatusCode > 0 {
   212  				assert.Equal(tt.expStatusCode, actualStatusCode, "Expected Status Code is incorrect")
   213  				return
   214  			}
   215  			if tt.expectedError {
   216  				assert.NotEmpty(err)
   217  				return
   218  			}
   219  		})
   220  	}
   221  }
   222  
   223  func TestEditSiteReplicationInfo(t *testing.T) {
   224  	assert := assert.New(t)
   225  	getResponse, err := makeExecuteReq("GET", nil)
   226  	if err != nil {
   227  		assert.Fail("Unable to get Site replication info")
   228  	}
   229  	getResObj := &models.SiteReplicationInfoResponse{}
   230  	json.NewDecoder(getResponse.Body).Decode(getResObj)
   231  	var secondDeploymentID string
   232  	fmt.Println("Edit Site Replication")
   233  	fmt.Println("Editing a valid site deployment id::", secondDeploymentID)
   234  	updatedSiteInfo := map[string]interface{}{
   235  		"deploymentID": secondDeploymentID,
   236  		"endpoint":     "http://minio2:9002", // replace it with docker name
   237  		// "endpoint": "http://localhost:9002", // local dev
   238  		"name": "sitellhost9002",
   239  	}
   240  	invalidUpdatedSiteInfo := map[string]interface{}{
   241  		"deploymentID": secondDeploymentID,
   242  		"endpoint":     "non-existenet:9002",
   243  		"name":         "sitellhost9002",
   244  	}
   245  	invalidUpdatedSiteInfo1 := map[string]interface{}{}
   246  	tests := []struct {
   247  		name          string
   248  		args          map[string]interface{}
   249  		expStatusCode int
   250  		expectedError bool
   251  	}{
   252  		{
   253  			name:          "Edit an existing valid site",
   254  			args:          updatedSiteInfo,
   255  			expStatusCode: -1,
   256  			expectedError: false,
   257  		},
   258  		{
   259  			name:          "Edit with an invalid site endpoint",
   260  			args:          invalidUpdatedSiteInfo,
   261  			expStatusCode: 500,
   262  			expectedError: false,
   263  		},
   264  		{
   265  			name:          "Edit with an invalid empty site ",
   266  			args:          invalidUpdatedSiteInfo1,
   267  			expStatusCode: 500,
   268  			expectedError: true,
   269  		},
   270  	}
   271  	for _, tt := range tests {
   272  		t.Run(tt.name, func(_ *testing.T) {
   273  			actualStatusCode, err := EditSiteReplicationInfo(tt.args)
   274  			fmt.Println("TestEditSiteReplicationInfo: ", actualStatusCode)
   275  			if tt.expStatusCode > 0 {
   276  				assert.Equal(tt.expStatusCode, actualStatusCode, "Expected Status Code is incorrect")
   277  				return
   278  			}
   279  			if tt.expectedError {
   280  				assert.NotEmpty(err)
   281  				return
   282  			}
   283  		})
   284  	}
   285  }
   286  
   287  func TestDeleteSiteReplicationInfo(t *testing.T) {
   288  	assert := assert.New(t)
   289  	delReq := map[string]interface{}{
   290  		"all": true,
   291  		"sites": []string{
   292  			"sitellhost9000",
   293  			"sitellhost9001",
   294  			"sitellhost9002",
   295  		},
   296  	}
   297  	invalidDelReq := map[string]interface{}{
   298  		"all": false,
   299  		"sites": []string{
   300  			"sitellhost9000",
   301  		},
   302  	}
   303  	invalidDelReq1 := map[string]interface{}{
   304  		"all":   true,
   305  		"sites": []string{},
   306  	}
   307  	tests := []struct {
   308  		name          string
   309  		args          map[string]interface{}
   310  		expStatusCode int
   311  		expectedError bool
   312  	}{
   313  		{
   314  			name:          "Delete Valid Sites already added",
   315  			args:          delReq,
   316  			expStatusCode: 204,
   317  			expectedError: false,
   318  		},
   319  		{
   320  			name:          "Delete Invalid Sites ",
   321  			args:          invalidDelReq,
   322  			expStatusCode: 500,
   323  			expectedError: true,
   324  		},
   325  		{
   326  			name:          "Invalid delete request",
   327  			args:          invalidDelReq1,
   328  			expStatusCode: 500,
   329  			expectedError: true,
   330  		},
   331  	}
   332  	fmt.Println("Delete Site Replication")
   333  	for _, tt := range tests {
   334  		t.Run(tt.name, func(_ *testing.T) {
   335  			actualStatusCode, err := DeleteSiteReplicationInfo(tt.args)
   336  			fmt.Println("TestDeleteSiteReplicationInfo: ", actualStatusCode)
   337  			if tt.expStatusCode > 0 {
   338  				assert.Equal(tt.expStatusCode, actualStatusCode, "Expected Status Code is incorrect")
   339  				return
   340  			}
   341  			if tt.expectedError {
   342  				assert.NotEmpty(err)
   343  				return
   344  			}
   345  		})
   346  	}
   347  }
   348  
   349  // Status API
   350  
   351  func makeStatusExecuteReq(method string, url string) (*http.Response, error) {
   352  	client := &http.Client{
   353  		Timeout: 10 * time.Second,
   354  	}
   355  	request, err := http.NewRequest(
   356  		method,
   357  		url,
   358  		nil,
   359  	)
   360  	if err != nil {
   361  		return nil, err
   362  	}
   363  	request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
   364  	request.Header.Add("Content-Type", "application/json")
   365  	response, err := client.Do(request)
   366  	if err != nil {
   367  		return nil, err
   368  	}
   369  	return response, nil
   370  }
   371  
   372  func TestGetSiteReplicationStatus(t *testing.T) {
   373  	assert := assert.New(t)
   374  
   375  	const (
   376  		baseAPIURL          = "http://localhost:9090/api/v1/admin/site-replication/status?"
   377  		lookupDefaultParams = "users=false&groups=false&buckets=false&policies=false&"
   378  	)
   379  
   380  	tests := []struct {
   381  		name          string
   382  		args          string
   383  		expStatusCode int
   384  		expectedError bool
   385  	}{
   386  		{
   387  			name:          "Default replication status",
   388  			args:          baseAPIURL + "users=true&groups=true&buckets=true&policies=true",
   389  			expStatusCode: 200,
   390  			expectedError: false,
   391  		},
   392  		{
   393  			name:          "Specific bucket  lookup replication status",
   394  			args:          baseAPIURL + lookupDefaultParams + "entityValue=test-bucket&entityType=bucket",
   395  			expStatusCode: 200,
   396  			expectedError: false,
   397  		},
   398  		{
   399  			name:          "Invalid specific bucket  lookup replication status",
   400  			args:          baseAPIURL + lookupDefaultParams + "entityValue=test-bucket-non-existent&entityType=bucket",
   401  			expStatusCode: 200,
   402  			expectedError: true,
   403  		},
   404  		{
   405  			name:          "Specific user lookup replication status",
   406  			args:          baseAPIURL + lookupDefaultParams + "entityValue=test-user&entityType=user",
   407  			expStatusCode: 200,
   408  			expectedError: false,
   409  		},
   410  		{
   411  			name:          "Invalid user lookup replication status",
   412  			args:          baseAPIURL + lookupDefaultParams + "entityValue=test-user-non-existent&entityType=user",
   413  			expStatusCode: 200,
   414  			expectedError: false,
   415  		},
   416  		{
   417  			name:          "Specific group lookup replication status",
   418  			args:          baseAPIURL + lookupDefaultParams + "entityValue=test-group&entityType=group",
   419  			expStatusCode: 200,
   420  			expectedError: false,
   421  		},
   422  		{
   423  			name:          "Invalid group lookup replication status",
   424  			args:          baseAPIURL + lookupDefaultParams + "entityValue=test-group-non-existent&entityType=group",
   425  			expStatusCode: 200,
   426  			expectedError: false,
   427  		},
   428  		{
   429  			name:          "Specific  policy replication status",
   430  			args:          baseAPIURL + lookupDefaultParams + "entityValue=consoleAdmin&entityType=policy",
   431  			expStatusCode: 200,
   432  			expectedError: false,
   433  		},
   434  		{
   435  			name:          "Invalid  policy replication status",
   436  			args:          baseAPIURL + lookupDefaultParams + "entityValue=test-policies&entityType=policy",
   437  			expStatusCode: 200,
   438  			expectedError: false,
   439  		},
   440  	}
   441  
   442  	for ti, tt := range tests {
   443  		t.Run(tt.name, func(_ *testing.T) {
   444  			response, err := makeStatusExecuteReq("GET", tt.args)
   445  			tgt := &models.SiteReplicationStatusResponse{}
   446  			json.NewDecoder(response.Body).Decode(tgt)
   447  			log.Println(err, response)
   448  			if err != nil {
   449  				log.Println(err)
   450  
   451  				if tt.expectedError {
   452  					assert.NotEmpty(err)
   453  				}
   454  				return
   455  			}
   456  			if response != nil {
   457  				assert.Equal(tt.expStatusCode, response.StatusCode, "Status Code for", ti)
   458  			}
   459  		})
   460  	}
   461  }