2021-04-18 12:41:13 -07:00
|
|
|
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
|
|
//
|
|
|
|
// This file is part of MinIO Object Storage stack
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2016-12-16 11:56:15 +05:30
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
2017-01-17 23:32:58 +05:30
|
|
|
"bytes"
|
2020-04-15 02:52:38 +02:00
|
|
|
"context"
|
2016-12-16 11:56:15 +05:30
|
|
|
"encoding/json"
|
2017-03-17 21:55:49 +05:30
|
|
|
"io"
|
2017-01-17 23:25:59 +01:00
|
|
|
"io/ioutil"
|
2016-12-16 11:56:15 +05:30
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2017-01-04 13:09:22 +05:30
|
|
|
"net/url"
|
2018-09-28 01:16:30 +01:00
|
|
|
"sync"
|
2016-12-16 11:56:15 +05:30
|
|
|
"testing"
|
|
|
|
|
2018-04-22 07:53:54 +05:30
|
|
|
"github.com/gorilla/mux"
|
2021-05-06 08:52:02 -07:00
|
|
|
"github.com/minio/madmin-go"
|
2021-06-01 14:59:40 -07:00
|
|
|
"github.com/minio/minio/internal/auth"
|
2016-12-16 11:56:15 +05:30
|
|
|
)
|
|
|
|
|
2020-06-12 20:04:01 -07:00
|
|
|
// adminErasureTestBed - encapsulates subsystems that need to be setup for
|
2017-01-23 14:02:55 +05:30
|
|
|
// admin-handler unit tests.
|
2020-06-12 20:04:01 -07:00
|
|
|
type adminErasureTestBed struct {
|
|
|
|
erasureDirs []string
|
|
|
|
objLayer ObjectLayer
|
|
|
|
router *mux.Router
|
2017-01-23 14:02:55 +05:30
|
|
|
}
|
|
|
|
|
2020-06-12 20:04:01 -07:00
|
|
|
// prepareAdminErasureTestBed - helper function that setups a single-node
|
|
|
|
// Erasure backend for admin-handler tests.
|
|
|
|
func prepareAdminErasureTestBed(ctx context.Context) (*adminErasureTestBed, error) {
|
2020-04-15 02:52:38 +02:00
|
|
|
|
2017-01-23 14:02:55 +05:30
|
|
|
// reset global variables to start afresh.
|
|
|
|
resetTestGlobals()
|
2017-02-07 12:51:43 -08:00
|
|
|
|
2020-06-12 20:04:01 -07:00
|
|
|
// Set globalIsErasure to indicate that the setup uses an erasure
|
2020-01-16 03:30:32 +01:00
|
|
|
// code backend.
|
2020-06-12 20:04:01 -07:00
|
|
|
globalIsErasure = true
|
2020-01-16 03:30:32 +01:00
|
|
|
|
2017-01-23 14:02:55 +05:30
|
|
|
// Initializing objectLayer for HealFormatHandler.
|
2020-06-12 20:04:01 -07:00
|
|
|
objLayer, erasureDirs, xlErr := initTestErasureObjLayer(ctx)
|
2017-01-23 14:02:55 +05:30
|
|
|
if xlErr != nil {
|
|
|
|
return nil, xlErr
|
|
|
|
}
|
|
|
|
|
2018-08-14 21:41:47 -07:00
|
|
|
// Initialize minio server config.
|
|
|
|
if err := newTestConfig(globalMinioDefaultRegion, objLayer); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-02-08 13:43:02 +05:30
|
|
|
// Initialize boot time
|
2017-03-18 23:58:41 +05:30
|
|
|
globalBootTime = UTCNow()
|
2017-02-08 13:43:02 +05:30
|
|
|
|
2021-01-26 20:47:42 -08:00
|
|
|
globalEndpoints = mustGetPoolEndpoints(erasureDirs...)
|
2017-01-23 14:02:55 +05:30
|
|
|
|
2020-05-20 10:18:15 -07:00
|
|
|
newAllSubsystems()
|
2019-02-12 01:25:52 -08:00
|
|
|
|
2020-06-09 19:19:03 -07:00
|
|
|
initAllSubsystems(ctx, objLayer)
|
2018-04-25 04:23:30 +05:30
|
|
|
|
2021-05-09 08:14:19 -07:00
|
|
|
globalIAMSys.InitStore(objLayer)
|
|
|
|
|
2017-01-23 14:02:55 +05:30
|
|
|
// Setup admin mgmt REST API handlers.
|
2018-04-22 07:53:54 +05:30
|
|
|
adminRouter := mux.NewRouter()
|
2020-05-25 00:17:52 -07:00
|
|
|
registerAdminRouter(adminRouter, true, true)
|
2017-01-23 14:02:55 +05:30
|
|
|
|
2020-06-12 20:04:01 -07:00
|
|
|
return &adminErasureTestBed{
|
|
|
|
erasureDirs: erasureDirs,
|
|
|
|
objLayer: objLayer,
|
|
|
|
router: adminRouter,
|
2017-01-23 14:02:55 +05:30
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TearDown - method that resets the test bed for subsequent unit
|
|
|
|
// tests to start afresh.
|
2020-06-12 20:04:01 -07:00
|
|
|
func (atb *adminErasureTestBed) TearDown() {
|
|
|
|
removeRoots(atb.erasureDirs)
|
2017-01-23 14:02:55 +05:30
|
|
|
resetTestGlobals()
|
|
|
|
}
|
|
|
|
|
2020-06-12 20:04:01 -07:00
|
|
|
// initTestObjLayer - Helper function to initialize an Erasure-based object
|
2017-01-23 14:02:55 +05:30
|
|
|
// layer and set globalObjectAPI.
|
2020-06-12 20:04:01 -07:00
|
|
|
func initTestErasureObjLayer(ctx context.Context) (ObjectLayer, []string, error) {
|
|
|
|
erasureDirs, err := getRandomDisks(16)
|
2018-02-15 17:45:57 -08:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2021-01-26 20:47:42 -08:00
|
|
|
endpoints := mustGetPoolEndpoints(erasureDirs...)
|
2018-06-06 12:52:56 -07:00
|
|
|
globalPolicySys = NewPolicySys()
|
2021-01-16 12:08:02 -08:00
|
|
|
objLayer, err := newErasureServerPools(ctx, endpoints)
|
2018-02-15 17:45:57 -08:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2017-01-23 14:02:55 +05:30
|
|
|
// Make objLayer available to all internal services via globalObjectAPI.
|
|
|
|
globalObjLayerMutex.Lock()
|
|
|
|
globalObjectAPI = objLayer
|
|
|
|
globalObjLayerMutex.Unlock()
|
2020-06-12 20:04:01 -07:00
|
|
|
return objLayer, erasureDirs, nil
|
2017-01-23 14:02:55 +05:30
|
|
|
}
|
|
|
|
|
2017-01-04 13:09:22 +05:30
|
|
|
// cmdType - Represents different service subcomands like status, stop
|
|
|
|
// and restart.
|
2016-12-16 11:56:15 +05:30
|
|
|
type cmdType int
|
|
|
|
|
|
|
|
const (
|
2019-08-28 15:04:43 -07:00
|
|
|
restartCmd cmdType = iota
|
2018-01-22 14:54:55 -08:00
|
|
|
stopCmd
|
2016-12-16 11:56:15 +05:30
|
|
|
)
|
|
|
|
|
2017-01-04 13:09:22 +05:30
|
|
|
// toServiceSignal - Helper function that translates a given cmdType
|
|
|
|
// value to its corresponding serviceSignal value.
|
2016-12-16 11:56:15 +05:30
|
|
|
func (c cmdType) toServiceSignal() serviceSignal {
|
|
|
|
switch c {
|
|
|
|
case restartCmd:
|
|
|
|
return serviceRestart
|
2018-01-22 14:54:55 -08:00
|
|
|
case stopCmd:
|
|
|
|
return serviceStop
|
2016-12-16 11:56:15 +05:30
|
|
|
}
|
2019-08-28 15:04:43 -07:00
|
|
|
return serviceRestart
|
2016-12-16 11:56:15 +05:30
|
|
|
}
|
|
|
|
|
2019-08-27 11:37:47 -07:00
|
|
|
func (c cmdType) toServiceAction() madmin.ServiceAction {
|
2018-01-22 14:54:55 -08:00
|
|
|
switch c {
|
|
|
|
case restartCmd:
|
2019-08-27 11:37:47 -07:00
|
|
|
return madmin.ServiceActionRestart
|
2018-01-22 14:54:55 -08:00
|
|
|
case stopCmd:
|
2019-08-27 11:37:47 -07:00
|
|
|
return madmin.ServiceActionStop
|
2018-01-22 14:54:55 -08:00
|
|
|
}
|
2019-08-28 15:04:43 -07:00
|
|
|
return madmin.ServiceActionRestart
|
2018-01-22 14:54:55 -08:00
|
|
|
}
|
|
|
|
|
2017-01-04 13:09:22 +05:30
|
|
|
// testServiceSignalReceiver - Helper function that simulates a
|
|
|
|
// go-routine waiting on service signal.
|
2016-12-16 11:56:15 +05:30
|
|
|
func testServiceSignalReceiver(cmd cmdType, t *testing.T) {
|
|
|
|
expectedCmd := cmd.toServiceSignal()
|
|
|
|
serviceCmd := <-globalServiceSignalCh
|
|
|
|
if serviceCmd != expectedCmd {
|
|
|
|
t.Errorf("Expected service command %v but received %v", expectedCmd, serviceCmd)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-04 13:09:22 +05:30
|
|
|
// getServiceCmdRequest - Constructs a management REST API request for service
|
|
|
|
// subcommands for a given cmdType value.
|
2019-08-27 11:37:47 -07:00
|
|
|
func getServiceCmdRequest(cmd cmdType, cred auth.Credentials) (*http.Request, error) {
|
|
|
|
queryVal := url.Values{}
|
|
|
|
queryVal.Set("action", string(cmd.toServiceAction()))
|
2019-11-04 09:30:59 -08:00
|
|
|
resource := adminPathPrefix + adminAPIVersionPrefix + "/service?" + queryVal.Encode()
|
2019-08-27 11:37:47 -07:00
|
|
|
req, err := newTestRequest(http.MethodPost, resource, 0, nil)
|
2016-12-16 11:56:15 +05:30
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-01-04 13:09:22 +05:30
|
|
|
|
|
|
|
// management REST API uses signature V4 for authentication.
|
2016-12-26 23:51:23 +05:30
|
|
|
err = signRequestV4(req, cred.AccessKey, cred.SecretKey)
|
2016-12-16 11:56:15 +05:30
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return req, nil
|
|
|
|
}
|
|
|
|
|
2017-01-04 13:09:22 +05:30
|
|
|
// testServicesCmdHandler - parametrizes service subcommand tests on
|
|
|
|
// cmdType value.
|
2017-01-24 17:08:36 +01:00
|
|
|
func testServicesCmdHandler(cmd cmdType, t *testing.T) {
|
2020-04-15 02:52:38 +02:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
defer cancel()
|
|
|
|
|
2020-06-12 20:04:01 -07:00
|
|
|
adminTestBed, err := prepareAdminErasureTestBed(ctx)
|
2016-12-16 11:56:15 +05:30
|
|
|
if err != nil {
|
2020-06-12 20:04:01 -07:00
|
|
|
t.Fatal("Failed to initialize a single node Erasure backend for admin handler tests.")
|
2016-12-16 11:56:15 +05:30
|
|
|
}
|
2017-01-23 14:02:55 +05:30
|
|
|
defer adminTestBed.TearDown()
|
2016-12-16 11:56:15 +05:30
|
|
|
|
2017-01-04 13:09:22 +05:30
|
|
|
// Initialize admin peers to make admin RPC calls. Note: In a
|
|
|
|
// single node setup, this degenerates to a simple function
|
|
|
|
// call under the hood.
|
2017-04-12 04:14:27 +05:30
|
|
|
globalMinioAddr = "127.0.0.1:9000"
|
2016-12-16 11:56:15 +05:30
|
|
|
|
2018-09-28 01:16:30 +01:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
2018-04-22 07:53:54 +05:30
|
|
|
// Setting up a go routine to simulate ServerRouter's
|
2016-12-16 11:56:15 +05:30
|
|
|
// handleServiceSignals for stop and restart commands.
|
2017-01-17 23:25:59 +01:00
|
|
|
if cmd == restartCmd {
|
2018-09-28 01:16:30 +01:00
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
testServiceSignalReceiver(cmd, t)
|
|
|
|
}()
|
2016-12-16 11:56:15 +05:30
|
|
|
}
|
2019-10-22 22:59:13 -07:00
|
|
|
credentials := globalActiveCred
|
2018-01-22 14:54:55 -08:00
|
|
|
|
2019-08-27 11:37:47 -07:00
|
|
|
req, err := getServiceCmdRequest(cmd, credentials)
|
2016-12-16 11:56:15 +05:30
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to build service status request %v", err)
|
|
|
|
}
|
2017-01-17 23:25:59 +01:00
|
|
|
|
|
|
|
rec := httptest.NewRecorder()
|
2018-04-22 07:53:54 +05:30
|
|
|
adminTestBed.router.ServeHTTP(rec, req)
|
2016-12-16 11:56:15 +05:30
|
|
|
|
2019-08-27 11:37:47 -07:00
|
|
|
if rec.Code != http.StatusOK {
|
|
|
|
resp, _ := ioutil.ReadAll(rec.Body)
|
|
|
|
t.Errorf("Expected to receive %d status code but received %d. Body (%s)",
|
|
|
|
http.StatusOK, rec.Code, string(resp))
|
|
|
|
}
|
2016-12-16 11:56:15 +05:30
|
|
|
|
2018-09-28 01:16:30 +01:00
|
|
|
// Wait until testServiceSignalReceiver() called in a goroutine quits.
|
|
|
|
wg.Wait()
|
2016-12-16 11:56:15 +05:30
|
|
|
}
|
|
|
|
|
2017-01-04 13:09:22 +05:30
|
|
|
// Test for service restart management REST API.
|
2016-12-16 11:56:15 +05:30
|
|
|
func TestServiceRestartHandler(t *testing.T) {
|
2017-01-24 17:08:36 +01:00
|
|
|
testServicesCmdHandler(restartCmd, t)
|
2017-01-17 23:25:59 +01:00
|
|
|
}
|
|
|
|
|
2017-03-17 21:55:49 +05:30
|
|
|
// buildAdminRequest - helper function to build an admin API request.
|
2018-01-22 14:54:55 -08:00
|
|
|
func buildAdminRequest(queryVal url.Values, method, path string,
|
2017-03-17 21:55:49 +05:30
|
|
|
contentLength int64, bodySeeker io.ReadSeeker) (*http.Request, error) {
|
2018-01-22 14:54:55 -08:00
|
|
|
|
|
|
|
req, err := newTestRequest(method,
|
2019-11-04 09:30:59 -08:00
|
|
|
adminPathPrefix+adminAPIVersionPrefix+path+"?"+queryVal.Encode(),
|
2018-01-22 14:54:55 -08:00
|
|
|
contentLength, bodySeeker)
|
2017-03-17 21:55:49 +05:30
|
|
|
if err != nil {
|
2018-04-05 15:04:40 -07:00
|
|
|
return nil, err
|
2017-03-17 21:55:49 +05:30
|
|
|
}
|
|
|
|
|
2019-10-22 22:59:13 -07:00
|
|
|
cred := globalActiveCred
|
2017-03-17 21:55:49 +05:30
|
|
|
err = signRequestV4(req, cred.AccessKey, cred.SecretKey)
|
|
|
|
if err != nil {
|
2018-04-05 15:04:40 -07:00
|
|
|
return nil, err
|
2017-03-17 21:55:49 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
return req, nil
|
|
|
|
}
|
|
|
|
|
2017-04-06 23:08:33 -07:00
|
|
|
func TestAdminServerInfo(t *testing.T) {
|
2020-04-15 02:52:38 +02:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
defer cancel()
|
|
|
|
|
2020-06-12 20:04:01 -07:00
|
|
|
adminTestBed, err := prepareAdminErasureTestBed(ctx)
|
2017-04-06 23:08:33 -07:00
|
|
|
if err != nil {
|
2020-06-12 20:04:01 -07:00
|
|
|
t.Fatal("Failed to initialize a single node Erasure backend for admin handler tests.")
|
2017-04-06 23:08:33 -07:00
|
|
|
}
|
2020-03-03 03:29:30 +03:00
|
|
|
|
2017-04-06 23:08:33 -07:00
|
|
|
defer adminTestBed.TearDown()
|
|
|
|
|
|
|
|
// Initialize admin peers to make admin RPC calls.
|
2017-04-12 04:14:27 +05:30
|
|
|
globalMinioAddr = "127.0.0.1:9000"
|
2017-04-06 23:08:33 -07:00
|
|
|
|
|
|
|
// Prepare query params for set-config mgmt REST API.
|
|
|
|
queryVal := url.Values{}
|
|
|
|
queryVal.Set("info", "")
|
|
|
|
|
2018-01-22 14:54:55 -08:00
|
|
|
req, err := buildAdminRequest(queryVal, http.MethodGet, "/info", 0, nil)
|
2017-04-06 23:08:33 -07:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to construct get-config object request - %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
rec := httptest.NewRecorder()
|
2018-04-22 07:53:54 +05:30
|
|
|
adminTestBed.router.ServeHTTP(rec, req)
|
2017-04-06 23:08:33 -07:00
|
|
|
if rec.Code != http.StatusOK {
|
|
|
|
t.Errorf("Expected to succeed but failed with %d", rec.Code)
|
|
|
|
}
|
|
|
|
|
2019-12-13 11:33:11 -08:00
|
|
|
results := madmin.InfoMessage{}
|
2017-04-21 15:15:53 +01:00
|
|
|
err = json.NewDecoder(rec.Body).Decode(&results)
|
2017-04-06 23:08:33 -07:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to decode set config result json %v", err)
|
|
|
|
}
|
|
|
|
|
2019-12-13 11:33:11 -08:00
|
|
|
if results.Region != globalMinioDefaultRegion {
|
|
|
|
t.Errorf("Expected %s, got %s", globalMinioDefaultRegion, results.Region)
|
2017-04-06 23:08:33 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-12 01:25:52 -08:00
|
|
|
// TestToAdminAPIErrCode - test for toAdminAPIErrCode helper function.
|
|
|
|
func TestToAdminAPIErrCode(t *testing.T) {
|
2017-02-28 01:10:27 +05:30
|
|
|
testCases := []struct {
|
|
|
|
err error
|
|
|
|
expectedAPIErr APIErrorCode
|
|
|
|
}{
|
|
|
|
// 1. Server not in quorum.
|
|
|
|
{
|
2020-06-12 20:04:01 -07:00
|
|
|
err: errErasureWriteQuorum,
|
2017-02-28 01:10:27 +05:30
|
|
|
expectedAPIErr: ErrAdminConfigNoQuorum,
|
|
|
|
},
|
|
|
|
// 2. No error.
|
|
|
|
{
|
|
|
|
err: nil,
|
|
|
|
expectedAPIErr: ErrNone,
|
|
|
|
},
|
|
|
|
// 3. Non-admin API specific error.
|
|
|
|
{
|
|
|
|
err: errDiskNotFound,
|
2020-04-09 09:30:02 -07:00
|
|
|
expectedAPIErr: toAPIErrorCode(GlobalContext, errDiskNotFound),
|
2017-02-28 01:10:27 +05:30
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range testCases {
|
2020-04-09 09:30:02 -07:00
|
|
|
actualErr := toAdminAPIErrCode(GlobalContext, test.err)
|
2017-02-28 01:10:27 +05:30
|
|
|
if actualErr != test.expectedAPIErr {
|
|
|
|
t.Errorf("Test %d: Expected %v but received %v",
|
|
|
|
i+1, test.expectedAPIErr, actualErr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-29 13:53:27 -07:00
|
|
|
|
|
|
|
func TestExtractHealInitParams(t *testing.T) {
|
|
|
|
mkParams := func(clientToken string, forceStart, forceStop bool) url.Values {
|
|
|
|
v := url.Values{}
|
|
|
|
if clientToken != "" {
|
2020-09-24 23:40:21 +08:00
|
|
|
v.Add(mgmtClientToken, clientToken)
|
2019-08-29 13:53:27 -07:00
|
|
|
}
|
|
|
|
if forceStart {
|
2020-09-24 23:40:21 +08:00
|
|
|
v.Add(mgmtForceStart, "")
|
2019-08-29 13:53:27 -07:00
|
|
|
}
|
|
|
|
if forceStop {
|
2020-09-24 23:40:21 +08:00
|
|
|
v.Add(mgmtForceStop, "")
|
2019-08-29 13:53:27 -07:00
|
|
|
}
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
qParmsArr := []url.Values{
|
|
|
|
// Invalid cases
|
|
|
|
mkParams("", true, true),
|
|
|
|
mkParams("111", true, true),
|
|
|
|
mkParams("111", true, false),
|
|
|
|
mkParams("111", false, true),
|
|
|
|
// Valid cases follow
|
|
|
|
mkParams("", true, false),
|
|
|
|
mkParams("", false, true),
|
|
|
|
mkParams("", false, false),
|
|
|
|
mkParams("111", false, false),
|
|
|
|
}
|
|
|
|
varsArr := []map[string]string{
|
|
|
|
// Invalid cases
|
2020-09-24 23:40:21 +08:00
|
|
|
{mgmtPrefix: "objprefix"},
|
2019-08-29 13:53:27 -07:00
|
|
|
// Valid cases
|
|
|
|
{},
|
2020-09-24 23:40:21 +08:00
|
|
|
{mgmtBucket: "bucket"},
|
|
|
|
{mgmtBucket: "bucket", mgmtPrefix: "objprefix"},
|
2019-08-29 13:53:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Body is always valid - we do not test JSON decoding.
|
|
|
|
body := `{"recursive": false, "dryRun": true, "remove": false, "scanMode": 0}`
|
|
|
|
|
|
|
|
// Test all combinations!
|
|
|
|
for pIdx, parms := range qParmsArr {
|
|
|
|
for vIdx, vars := range varsArr {
|
2020-12-26 22:58:06 -08:00
|
|
|
_, err := extractHealInitParams(vars, parms, bytes.NewReader([]byte(body)))
|
2019-08-29 13:53:27 -07:00
|
|
|
isErrCase := false
|
|
|
|
if pIdx < 4 || vIdx < 1 {
|
|
|
|
isErrCase = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != ErrNone && !isErrCase {
|
|
|
|
t.Errorf("Got unexpected error: %v %v %v", pIdx, vIdx, err)
|
|
|
|
} else if err == ErrNone && isErrCase {
|
|
|
|
t.Errorf("Got no error but expected one: %v %v", pIdx, vIdx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|