1308 lines
32 KiB
Go
1308 lines
32 KiB
Go
// This file is part of MinIO Console Server
|
|
// Copyright (c) 2021 MinIO, Inc.
|
|
//
|
|
// 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/>.
|
|
|
|
package operatorintegration
|
|
|
|
import (
|
|
"bytes"
|
|
b64 "encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-openapi/loads"
|
|
"github.com/minio/console/models"
|
|
"github.com/minio/console/operatorapi"
|
|
"github.com/minio/console/operatorapi/operations"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
var (
|
|
token string
|
|
jwt string
|
|
)
|
|
|
|
func inspectHTTPResponse(httpResponse *http.Response) string {
|
|
/*
|
|
Helper function to inspect the content of a HTTP response.
|
|
*/
|
|
b, err := io.ReadAll(httpResponse.Body)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
return "Http Response: " + string(b)
|
|
}
|
|
|
|
func decodeBase64(value string) string {
|
|
/*
|
|
Helper function to decode in base64
|
|
*/
|
|
result, err := b64.StdEncoding.DecodeString(value)
|
|
if err != nil {
|
|
log.Fatal("error:", err)
|
|
}
|
|
return string(result)
|
|
}
|
|
|
|
func printLoggingMessage(message string, functionName string) {
|
|
/*
|
|
Helper function to have standard output across the tests.
|
|
*/
|
|
finalString := "......................." + functionName + "(): " + message
|
|
fmt.Println(finalString)
|
|
}
|
|
|
|
func printStartFunc(functionName string) {
|
|
/*
|
|
Common function for all tests to tell that test has started
|
|
*/
|
|
fmt.Println("")
|
|
printLoggingMessage("started", functionName)
|
|
}
|
|
|
|
func printEndFunc(functionName string) {
|
|
/*
|
|
Helper function for all tests to tell that test has ended, is completed
|
|
*/
|
|
printLoggingMessage("completed", functionName)
|
|
fmt.Println("")
|
|
}
|
|
|
|
func initConsoleServer() (*operatorapi.Server, error) {
|
|
// os.Setenv("CONSOLE_MINIO_SERVER", "localhost:9000")
|
|
|
|
swaggerSpec, err := loads.Embedded(operatorapi.SwaggerJSON, operatorapi.FlatSwaggerJSON)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
noLog := func(string, ...interface{}) {
|
|
// nothing to log
|
|
}
|
|
|
|
// Initialize MinIO loggers
|
|
operatorapi.LogInfo = noLog
|
|
operatorapi.LogError = noLog
|
|
|
|
api := operations.NewOperatorAPI(swaggerSpec)
|
|
api.Logger = noLog
|
|
|
|
server := operatorapi.NewServer(api)
|
|
// register all APIs
|
|
server.ConfigureAPI()
|
|
|
|
consolePort, _ := strconv.Atoi("9090")
|
|
|
|
server.Host = "0.0.0.0"
|
|
server.Port = consolePort
|
|
operatorapi.Port = "9090"
|
|
operatorapi.Hostname = "0.0.0.0"
|
|
|
|
return server, nil
|
|
}
|
|
|
|
func TestMain(m *testing.M) {
|
|
printStartFunc("TestMain")
|
|
// start console server
|
|
go func() {
|
|
fmt.Println("start server")
|
|
srv, err := initConsoleServer()
|
|
fmt.Println("Server has been started at this point")
|
|
if err != nil {
|
|
fmt.Println("There is an error in console server: ", err)
|
|
log.Println(err)
|
|
log.Println("init fail")
|
|
return
|
|
}
|
|
fmt.Println("Start serving with Serve() function")
|
|
srv.Serve()
|
|
fmt.Println("After Serve() function")
|
|
}()
|
|
|
|
fmt.Println("sleeping")
|
|
time.Sleep(2 * time.Second)
|
|
fmt.Println("after 2 seconds sleep")
|
|
|
|
fmt.Println("creating the client")
|
|
|
|
// SA_TOKEN=$(kubectl -n minio-operator get secret console-sa-secret -o jsonpath="{.data.token}" | base64 --decode)
|
|
fmt.Println("Where we have the secret already: ")
|
|
app2 := "kubectl"
|
|
argu0 := "--namespace"
|
|
argu1 := "minio-operator"
|
|
argu2 := "get"
|
|
argu3 := "secret"
|
|
argu4 := "console-sa-secret"
|
|
argu5 := "-o"
|
|
argu6 := "jsonpath=\"{.data.token}\""
|
|
fmt.Println("Prior executing second command to get the token")
|
|
cmd2 := exec.Command(app2, argu0, argu1, argu2, argu3, argu4, argu5, argu6)
|
|
fmt.Println("after executing second command to get the token")
|
|
var out2 bytes.Buffer
|
|
var stderr2 bytes.Buffer
|
|
cmd2.Stdout = &out2
|
|
cmd2.Stderr = &stderr2
|
|
err2 := cmd2.Run()
|
|
if err2 != nil {
|
|
fmt.Println(fmt.Sprint(err2) + ": -> " + stderr2.String())
|
|
return
|
|
}
|
|
secret2 := out2.String()
|
|
jwt := decodeBase64(secret2[1 : len(secret2)-1])
|
|
if jwt == "" {
|
|
fmt.Println("jwt cannot be empty string")
|
|
os.Exit(-1)
|
|
}
|
|
response, err := LoginOperator()
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
|
|
if response != nil {
|
|
for _, cookie := range response.Cookies() {
|
|
if cookie.Name == "token" {
|
|
token = cookie.Value
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if token == "" {
|
|
log.Println("authentication token not found in cookies response")
|
|
return
|
|
}
|
|
|
|
code := m.Run()
|
|
printEndFunc("TestMain")
|
|
os.Exit(code)
|
|
}
|
|
|
|
func ListTenants() (*http.Response, error) {
|
|
/*
|
|
Helper function to list buckets
|
|
HTTP Verb: GET
|
|
URL: http://localhost:9090/api/v1/tenants
|
|
*/
|
|
request, err := http.NewRequest(
|
|
"GET", "http://localhost:9090/api/v1/tenants", nil)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func TestListTenants(t *testing.T) {
|
|
// Tenants can be listed via API: https://github.com/miniohq/engineering/issues/591
|
|
printStartFunc("TestListTenants")
|
|
assert := assert.New(t)
|
|
resp, err := ListTenants()
|
|
assert.Nil(err)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200, resp.StatusCode, "Status Code is incorrect")
|
|
}
|
|
bodyBytes, _ := ioutil.ReadAll(resp.Body)
|
|
result := models.ListTenantsResponse{}
|
|
err = json.Unmarshal(bodyBytes, &result)
|
|
if err != nil {
|
|
log.Println(err)
|
|
assert.Nil(err)
|
|
}
|
|
TenantName := &result.Tenants[0].Name // The array has to be empty, no index accessible
|
|
fmt.Println(*TenantName)
|
|
assert.Equal("storage-kms-encrypted", *TenantName, *TenantName)
|
|
printEndFunc("TestListTenants")
|
|
}
|
|
|
|
func CreateTenant(tenantName string, namespace string, accessKey string, secretKey string, accessKeys []string, idp map[string]interface{}, tls map[string]interface{}, prometheusConfiguration map[string]interface{}, logSearchConfiguration map[string]interface{}, erasureCodingParity int, pools []map[string]interface{}, exposeConsole bool, exposeMinIO bool, image string, serviceName string, enablePrometheus bool, enableConsole bool, enableTLS bool, secretKeys []string) (*http.Response, error) {
|
|
/*
|
|
Helper function to create a tenant
|
|
HTTP Verb: POST
|
|
API: /api/v1/tenants
|
|
*/
|
|
requestDataAdd := map[string]interface{}{
|
|
"name": tenantName,
|
|
"namespace": namespace,
|
|
"access_key": accessKey,
|
|
"secret_key": secretKey,
|
|
"access_keys": accessKeys,
|
|
"secret_keys": secretKeys,
|
|
"enable_tls": enableTLS,
|
|
"enable_console": enableConsole,
|
|
"enable_prometheus": enablePrometheus,
|
|
"service_name": serviceName,
|
|
"image": image,
|
|
"expose_minio": exposeMinIO,
|
|
"expose_console": exposeConsole,
|
|
"pools": pools,
|
|
"erasureCodingParity": erasureCodingParity,
|
|
"logSearchConfiguration": logSearchConfiguration,
|
|
"prometheusConfiguration": prometheusConfiguration,
|
|
"tls": tls,
|
|
"idp": idp,
|
|
}
|
|
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
|
requestDataBody := bytes.NewReader(requestDataJSON)
|
|
request, err := http.NewRequest(
|
|
"POST",
|
|
"http://localhost:9090/api/v1/tenants",
|
|
requestDataBody,
|
|
)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func DeleteTenant(nameSpace, tenant string) (*http.Response, error) {
|
|
/*
|
|
URL: /namespaces/{namespace}/tenants/{tenant}:
|
|
HTTP Verb: DELETE
|
|
Summary: Delete tenant and underlying pvcs
|
|
*/
|
|
request, err := http.NewRequest(
|
|
"DELETE",
|
|
"http://localhost:9090/api/v1/namespaces/"+nameSpace+"/tenants/"+tenant,
|
|
nil,
|
|
)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func TestCreateTenant(t *testing.T) {
|
|
printStartFunc("TestCreateTenant")
|
|
|
|
// Variables
|
|
assert := assert.New(t)
|
|
erasureCodingParity := 2
|
|
tenantName := "new-tenant"
|
|
namespace := "default"
|
|
accessKey := ""
|
|
secretKey := ""
|
|
var accessKeys []string
|
|
var secretKeys []string
|
|
var minio []string
|
|
var caCertificates []string
|
|
var consoleCAcertificates []string
|
|
enableTLS := true
|
|
enableConsole := true
|
|
enablePrometheus := true
|
|
serviceName := ""
|
|
image := ""
|
|
exposeMinIO := true
|
|
exposeConsole := true
|
|
values := make([]string, 1)
|
|
values[0] = "new-tenant"
|
|
values2 := make([]string, 1)
|
|
values2[0] = "pool-0"
|
|
keys := make([]map[string]interface{}, 1)
|
|
keys[0] = map[string]interface{}{
|
|
"access_key": "IGLksSXdiU3fjcRI",
|
|
"secret_key": "EqeCPZ1xBYdnygizxxRWnkH09N2350nO",
|
|
}
|
|
pools := make([]map[string]interface{}, 1)
|
|
matchExpressions := make([]map[string]interface{}, 2)
|
|
matchExpressions[0] = map[string]interface{}{
|
|
"key": "v1.min.io/tenant",
|
|
"operator": "In",
|
|
"values": values,
|
|
}
|
|
matchExpressions[1] = map[string]interface{}{
|
|
"key": "v1.min.io/pool",
|
|
"operator": "In",
|
|
"values": values2,
|
|
}
|
|
requiredDuringSchedulingIgnoredDuringExecution := make([]map[string]interface{}, 1)
|
|
requiredDuringSchedulingIgnoredDuringExecution[0] = map[string]interface{}{
|
|
"labelSelector": map[string]interface{}{
|
|
"matchExpressions": matchExpressions,
|
|
},
|
|
"topologyKey": "kubernetes.io/hostname",
|
|
}
|
|
pools0 := map[string]interface{}{
|
|
"name": "pool-0",
|
|
"servers": 4,
|
|
"volumes_per_server": 1,
|
|
"volume_configuration": map[string]interface{}{
|
|
"size": 26843545600,
|
|
"storage_class_name": "standard",
|
|
},
|
|
"securityContext": nil,
|
|
"affinity": map[string]interface{}{
|
|
"podAntiAffinity": map[string]interface{}{
|
|
"requiredDuringSchedulingIgnoredDuringExecution": requiredDuringSchedulingIgnoredDuringExecution,
|
|
},
|
|
},
|
|
"resources": map[string]interface{}{
|
|
"requests": map[string]interface{}{
|
|
"cpu": 2,
|
|
"memory": 2,
|
|
},
|
|
},
|
|
}
|
|
logSearchConfiguration := map[string]interface{}{
|
|
"image": "",
|
|
"postgres_image": "",
|
|
"postgres_init_image": "",
|
|
}
|
|
prometheusConfiguration := map[string]interface{}{
|
|
"image": "",
|
|
"sidecar_image": "",
|
|
"init_image": "",
|
|
}
|
|
tls := map[string]interface{}{
|
|
"minio": minio,
|
|
"ca_certificates": caCertificates,
|
|
"console_ca_certificates": consoleCAcertificates,
|
|
}
|
|
idp := map[string]interface{}{
|
|
"keys": keys,
|
|
}
|
|
pools[0] = pools0
|
|
|
|
// 1. Create Tenant
|
|
resp, err := CreateTenant(
|
|
tenantName,
|
|
namespace,
|
|
accessKey,
|
|
secretKey,
|
|
accessKeys,
|
|
idp,
|
|
tls,
|
|
prometheusConfiguration,
|
|
logSearchConfiguration,
|
|
erasureCodingParity,
|
|
pools,
|
|
exposeConsole,
|
|
exposeMinIO,
|
|
image,
|
|
serviceName,
|
|
enablePrometheus,
|
|
enableConsole,
|
|
enableTLS,
|
|
secretKeys,
|
|
)
|
|
assert.Nil(err)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200, resp.StatusCode, "Status Code is incorrect")
|
|
}
|
|
|
|
printEndFunc("TestCreateTenant")
|
|
}
|
|
|
|
func TestDeleteTenant(t *testing.T) {
|
|
printStartFunc("TestCreateTenant")
|
|
|
|
// Variables
|
|
assert := assert.New(t)
|
|
erasureCodingParity := 2
|
|
tenantName := "new-tenant-3"
|
|
namespace := "new-namespace-3"
|
|
|
|
// 0. Create the namespace
|
|
resp, err := CreateNamespace(namespace)
|
|
assert.Nil(err)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
201, resp.StatusCode, inspectHTTPResponse(resp))
|
|
}
|
|
|
|
accessKey := ""
|
|
secretKey := ""
|
|
var accessKeys []string
|
|
var secretKeys []string
|
|
var minio []string
|
|
var caCertificates []string
|
|
var consoleCAcertificates []string
|
|
enableTLS := true
|
|
enableConsole := true
|
|
enablePrometheus := true
|
|
serviceName := ""
|
|
image := ""
|
|
exposeMinIO := true
|
|
exposeConsole := true
|
|
values := make([]string, 1)
|
|
values[0] = "new-tenant"
|
|
values2 := make([]string, 1)
|
|
values2[0] = "pool-0"
|
|
keys := make([]map[string]interface{}, 1)
|
|
keys[0] = map[string]interface{}{
|
|
"access_key": "IGLksSXdiU3fjcRI",
|
|
"secret_key": "EqeCPZ1xBYdnygizxxRWnkH09N2350nO",
|
|
}
|
|
pools := make([]map[string]interface{}, 1)
|
|
matchExpressions := make([]map[string]interface{}, 2)
|
|
matchExpressions[0] = map[string]interface{}{
|
|
"key": "v1.min.io/tenant",
|
|
"operator": "In",
|
|
"values": values,
|
|
}
|
|
matchExpressions[1] = map[string]interface{}{
|
|
"key": "v1.min.io/pool",
|
|
"operator": "In",
|
|
"values": values2,
|
|
}
|
|
requiredDuringSchedulingIgnoredDuringExecution := make([]map[string]interface{}, 1)
|
|
requiredDuringSchedulingIgnoredDuringExecution[0] = map[string]interface{}{
|
|
"labelSelector": map[string]interface{}{
|
|
"matchExpressions": matchExpressions,
|
|
},
|
|
"topologyKey": "kubernetes.io/hostname",
|
|
}
|
|
pools0 := map[string]interface{}{
|
|
"name": "pool-0",
|
|
"servers": 4,
|
|
"volumes_per_server": 1,
|
|
"volume_configuration": map[string]interface{}{
|
|
"size": 26843545600,
|
|
"storage_class_name": "standard",
|
|
},
|
|
"securityContext": nil,
|
|
"affinity": map[string]interface{}{
|
|
"podAntiAffinity": map[string]interface{}{
|
|
"requiredDuringSchedulingIgnoredDuringExecution": requiredDuringSchedulingIgnoredDuringExecution,
|
|
},
|
|
},
|
|
"resources": map[string]interface{}{
|
|
"requests": map[string]interface{}{
|
|
"cpu": 2,
|
|
"memory": 2,
|
|
},
|
|
},
|
|
}
|
|
logSearchConfiguration := map[string]interface{}{
|
|
"image": "",
|
|
"postgres_image": "",
|
|
"postgres_init_image": "",
|
|
}
|
|
prometheusConfiguration := map[string]interface{}{
|
|
"image": "",
|
|
"sidecar_image": "",
|
|
"init_image": "",
|
|
}
|
|
tls := map[string]interface{}{
|
|
"minio": minio,
|
|
"ca_certificates": caCertificates,
|
|
"console_ca_certificates": consoleCAcertificates,
|
|
}
|
|
idp := map[string]interface{}{
|
|
"keys": keys,
|
|
}
|
|
pools[0] = pools0
|
|
|
|
// 1. Create Tenant
|
|
resp, err = CreateTenant(
|
|
tenantName,
|
|
namespace,
|
|
accessKey,
|
|
secretKey,
|
|
accessKeys,
|
|
idp,
|
|
tls,
|
|
prometheusConfiguration,
|
|
logSearchConfiguration,
|
|
erasureCodingParity,
|
|
pools,
|
|
exposeConsole,
|
|
exposeMinIO,
|
|
image,
|
|
serviceName,
|
|
enablePrometheus,
|
|
enableConsole,
|
|
enableTLS,
|
|
secretKeys,
|
|
)
|
|
assert.Nil(err)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200, resp.StatusCode, "Status Code is incorrect")
|
|
}
|
|
|
|
// 2. Delete tenant
|
|
resp, err = DeleteTenant(namespace, tenantName)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
204,
|
|
resp.StatusCode,
|
|
inspectHTTPResponse(resp),
|
|
)
|
|
}
|
|
|
|
printEndFunc("TestCreateTenant")
|
|
}
|
|
|
|
func ListTenantsByNameSpace(namespace string) (*http.Response, error) {
|
|
/*
|
|
Helper function to list buckets
|
|
HTTP Verb: GET
|
|
URL: http://localhost:9090/api/v1/namespaces/{namespace}/tenants
|
|
*/
|
|
request, err := http.NewRequest(
|
|
"GET", "http://localhost:9090/api/v1/namespaces/"+namespace+"/tenants", nil)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func TestListTenantsByNameSpace(t *testing.T) {
|
|
assert := assert.New(t)
|
|
namespace := "default"
|
|
resp, err := ListTenantsByNameSpace(namespace)
|
|
assert.Nil(err)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200, resp.StatusCode, "Status Code is incorrect")
|
|
}
|
|
bodyBytes, _ := ioutil.ReadAll(resp.Body)
|
|
result := models.ListTenantsResponse{}
|
|
err = json.Unmarshal(bodyBytes, &result)
|
|
if err != nil {
|
|
log.Println(err)
|
|
assert.Nil(err)
|
|
}
|
|
if len(result.Tenants) == 0 {
|
|
assert.Fail("FAIL: There are no tenants in the array")
|
|
}
|
|
TenantName := &result.Tenants[0].Name
|
|
fmt.Println(*TenantName)
|
|
assert.Equal("new-tenant", *TenantName, *TenantName)
|
|
}
|
|
|
|
func ListNodeLabels() (*http.Response, error) {
|
|
/*
|
|
Helper function to list buckets
|
|
HTTP Verb: GET
|
|
URL: http://localhost:9090/api/v1/nodes/labels
|
|
*/
|
|
request, err := http.NewRequest(
|
|
"GET", "http://localhost:9090/api/v1/nodes/labels", nil)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func TestListNodeLabels(t *testing.T) {
|
|
assert := assert.New(t)
|
|
resp, err := ListNodeLabels()
|
|
assert.Nil(err)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
finalResponse := inspectHTTPResponse(resp)
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200, resp.StatusCode, finalResponse)
|
|
}
|
|
// "beta.kubernetes.io/arch" is a label of our nodes and is expected
|
|
assert.True(
|
|
strings.Contains(finalResponse, "beta.kubernetes.io/arch"),
|
|
finalResponse)
|
|
}
|
|
|
|
func GetPodEvents(nameSpace string, tenant string, podName string) (*http.Response, error) {
|
|
/*
|
|
Helper function to get events for pod
|
|
URL: /namespaces/{namespace}/tenants/{tenant}/pods/{podName}/events
|
|
HTTP Verb: GET
|
|
*/
|
|
request, err := http.NewRequest(
|
|
"GET", "http://localhost:9090/api/v1/namespaces/"+nameSpace+"/tenants/"+tenant+"/pods/"+podName+"/events", nil)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func TestGetPodEvents(t *testing.T) {
|
|
assert := assert.New(t)
|
|
namespace := "tenant-lite"
|
|
tenant := "storage-lite"
|
|
podName := "storage-lite-pool-0-0"
|
|
resp, err := GetPodEvents(namespace, tenant, podName)
|
|
assert.Nil(err)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200, resp.StatusCode, "Status Code is incorrect")
|
|
}
|
|
}
|
|
|
|
func GetPodDescribe(nameSpace string, tenant string, podName string) (*http.Response, error) {
|
|
/*
|
|
Helper function to get events for pod
|
|
URL: /namespaces/{namespace}/tenants/{tenant}/pods/{podName}/events
|
|
HTTP Verb: GET
|
|
*/
|
|
fmt.Println(nameSpace)
|
|
fmt.Println(tenant)
|
|
fmt.Println(podName)
|
|
request, err := http.NewRequest(
|
|
"GET", "http://localhost:9090/api/v1/namespaces/"+nameSpace+"/tenants/"+tenant+"/pods/"+podName+"/describe", nil)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func TestGetPodDescribe(t *testing.T) {
|
|
assert := assert.New(t)
|
|
namespace := "tenant-lite"
|
|
tenant := "storage-lite"
|
|
podName := "storage-lite-pool-0-0"
|
|
resp, err := GetPodDescribe(namespace, tenant, podName)
|
|
assert.Nil(err)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
finalResponse := inspectHTTPResponse(resp)
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200, resp.StatusCode, finalResponse)
|
|
}
|
|
/*if resp != nil {
|
|
assert.Equal(
|
|
200, resp.StatusCode, "Status Code is incorrect")
|
|
}*/
|
|
}
|
|
|
|
func GetCSR(nameSpace string, tenant string) (*http.Response, error) {
|
|
/*
|
|
Helper function to get events for pod
|
|
URL: /namespaces/{namespace}/tenants/{tenant}/csr
|
|
HTTP Verb: GET
|
|
*/
|
|
request, err := http.NewRequest(
|
|
"GET", "http://localhost:9090/api/v1/namespaces/"+nameSpace+"/tenants/"+tenant+"/csr/", nil)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func TestGetCSR(t *testing.T) {
|
|
assert := assert.New(t)
|
|
namespace := "tenant-lite"
|
|
tenant := "storage-lite"
|
|
resp, err := GetCSR(namespace, tenant)
|
|
assert.Nil(err)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
finalResponse := inspectHTTPResponse(resp)
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200, resp.StatusCode, finalResponse)
|
|
}
|
|
assert.Equal(strings.Contains(finalResponse, "Automatically approved by MinIO Operator"), true, finalResponse)
|
|
}
|
|
|
|
func TestGetMultipleCSRs(t *testing.T) {
|
|
/*
|
|
We can have multiple CSRs per tenant, the idea is to support them in our API and test them here, making sure we
|
|
can retrieve them all, as an example I found this tenant:
|
|
storage-kms-encrypted -client -tenant-kms-encrypted-csr
|
|
storage-kms-encrypted -kes -tenant-kms-encrypted-csr
|
|
storage-kms-encrypted -tenant-kms-encrypted-csr
|
|
Notice the nomenclature of it:
|
|
<tenant-name>-<*>-<namespace>-csr
|
|
where * is anything either nothing or something, anything.
|
|
*/
|
|
assert := assert.New(t)
|
|
namespace := "tenant-kms-encrypted"
|
|
tenant := "storage-kms-encrypted"
|
|
resp, err := GetCSR(namespace, tenant)
|
|
assert.Nil(err)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
finalResponse := inspectHTTPResponse(resp)
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200, resp.StatusCode, finalResponse)
|
|
}
|
|
var expectedMessages [3]string
|
|
expectedMessages[0] = "storage-kms-encrypted-tenant-kms-encrypted-csr"
|
|
expectedMessages[1] = "storage-kms-encrypted-kes-tenant-kms-encrypted-csr"
|
|
expectedMessages[2] = "Automatically approved by MinIO Operator"
|
|
for _, element := range expectedMessages {
|
|
assert.Equal(strings.Contains(finalResponse, element), true)
|
|
}
|
|
}
|
|
|
|
func ListPVCsForTenant(nameSpace string, tenant string) (*http.Response, error) {
|
|
/*
|
|
URL: /namespaces/{namespace}/tenants/{tenant}/pvcs
|
|
HTTP Verb: GET
|
|
*/
|
|
request, err := http.NewRequest(
|
|
"GET", "http://localhost:9090/api/v1/namespaces/"+nameSpace+"/tenants/"+tenant+"/pvcs/", nil)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func TestListPVCsForTenant(t *testing.T) {
|
|
/*
|
|
Function to list and verify the Tenant's Persistent Volume Claims
|
|
*/
|
|
assert := assert.New(t)
|
|
namespace := "tenant-lite"
|
|
tenant := "storage-lite"
|
|
resp, err := ListPVCsForTenant(namespace, tenant)
|
|
bodyResponse := resp.Body
|
|
assert.Nil(err)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200, resp.StatusCode, "failed")
|
|
}
|
|
bodyBytes, _ := ioutil.ReadAll(bodyResponse)
|
|
listObjs := models.ListPVCsResponse{}
|
|
err = json.Unmarshal(bodyBytes, &listObjs)
|
|
if err != nil {
|
|
log.Println(err)
|
|
assert.Nil(err)
|
|
}
|
|
var pvcArray [4]string
|
|
pvcArray[0] = "data0-storage-lite-pool-0-0"
|
|
pvcArray[1] = "data0-storage-lite-pool-0-1"
|
|
pvcArray[2] = "data0-storage-lite-pool-0-2"
|
|
pvcArray[3] = "data0-storage-lite-pool-0-3"
|
|
for i := 0; i < len(pvcArray); i++ {
|
|
assert.Equal(strings.Contains(listObjs.Pvcs[i].Name, pvcArray[i]), true)
|
|
}
|
|
}
|
|
|
|
func CreateNamespace(nameSpace string) (*http.Response, error) {
|
|
/*
|
|
Description: Creates a new Namespace with given information
|
|
URL: /namespace
|
|
HTTP Verb: POST
|
|
*/
|
|
requestDataAdd := map[string]interface{}{
|
|
"name": nameSpace,
|
|
}
|
|
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
|
requestDataBody := bytes.NewReader(requestDataJSON)
|
|
request, err := http.NewRequest(
|
|
"POST",
|
|
"http://localhost:9090/api/v1/namespace/",
|
|
requestDataBody,
|
|
)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func TestCreateNamespace(t *testing.T) {
|
|
/*
|
|
Function to Create a Namespace only once.
|
|
*/
|
|
assert := assert.New(t)
|
|
namespace := "new-namespace-thujun2208pm"
|
|
tests := []struct {
|
|
name string
|
|
nameSpace string
|
|
expectedStatus int
|
|
}{
|
|
{
|
|
name: "Create Namespace for the first time",
|
|
expectedStatus: 201,
|
|
nameSpace: namespace,
|
|
},
|
|
{
|
|
name: "Create repeated namespace for second time",
|
|
expectedStatus: 500,
|
|
nameSpace: namespace,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
resp, err := CreateNamespace(tt.nameSpace)
|
|
assert.Nil(err)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
tt.expectedStatus, resp.StatusCode, "failed")
|
|
} else {
|
|
assert.Fail("resp cannot be nil")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func LoginOperator() (*http.Response, error) {
|
|
/*
|
|
Description: Login to Operator Console.
|
|
URL: /login/operator
|
|
Params in the Body: jwt
|
|
*/
|
|
requestData := map[string]string{
|
|
"jwt": jwt,
|
|
}
|
|
fmt.Println("requestData: ", requestData)
|
|
|
|
requestDataJSON, _ := json.Marshal(requestData)
|
|
|
|
requestDataBody := bytes.NewReader(requestDataJSON)
|
|
|
|
request, err := http.NewRequest("POST", "http://localhost:9090/api/v1/login/operator", requestDataBody)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func LogoutOperator() (*http.Response, error) {
|
|
/*
|
|
Description: Logout from Operator.
|
|
URL: /logout
|
|
*/
|
|
request, err := http.NewRequest(
|
|
"POST",
|
|
"http://localhost:9090/api/v1/logout",
|
|
nil,
|
|
)
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func TestLogout(t *testing.T) {
|
|
// Vars
|
|
assert := assert.New(t)
|
|
|
|
// 1. Logout
|
|
response, err := LogoutOperator()
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if response != nil {
|
|
assert.Equal(
|
|
200,
|
|
response.StatusCode,
|
|
inspectHTTPResponse(response),
|
|
)
|
|
}
|
|
|
|
// 2. Login to recover token
|
|
response, err = LoginOperator()
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if response != nil {
|
|
for _, cookie := range response.Cookies() {
|
|
if cookie.Name == "token" {
|
|
token = cookie.Value
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify token
|
|
if token == "" {
|
|
assert.Fail("authentication token not found in cookies response")
|
|
}
|
|
}
|
|
|
|
func EnableTenantLogging(namespace, tenant string) (*http.Response, error) {
|
|
/*
|
|
Description: Enable Tenant Logging
|
|
HTTP Verb: POST
|
|
*/
|
|
request, err := http.NewRequest(
|
|
"POST",
|
|
"http://localhost:9090/api/v1/namespaces/"+namespace+"/tenants/"+tenant+"/enable-logging",
|
|
nil,
|
|
)
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func DisableTenantLogging(namespace, tenant string) (*http.Response, error) {
|
|
/*
|
|
Description: Disable Tenant Logging
|
|
*/
|
|
request, err := http.NewRequest(
|
|
"POST",
|
|
"http://localhost:9090/api/v1/namespaces/"+namespace+"/tenants/"+tenant+"/disable-logging",
|
|
nil,
|
|
)
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func TestEnableTenantLogging(t *testing.T) {
|
|
// Vars
|
|
assert := assert.New(t)
|
|
namespace := "tenant-lite"
|
|
tenant := "storage-lite"
|
|
|
|
// Enable tenant logging
|
|
resp, err := EnableTenantLogging(namespace, tenant)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200,
|
|
resp.StatusCode,
|
|
inspectHTTPResponse(resp),
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestDisableTenantLogging(t *testing.T) {
|
|
// Vars
|
|
assert := assert.New(t)
|
|
namespace := "tenant-lite"
|
|
tenant := "storage-lite"
|
|
|
|
// Disable tenant logging
|
|
resp, err := DisableTenantLogging(namespace, tenant)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200,
|
|
resp.StatusCode,
|
|
inspectHTTPResponse(resp),
|
|
)
|
|
}
|
|
}
|
|
|
|
func GetTenantLogs(nameSpace, tenant string) (*http.Response, error) {
|
|
/*
|
|
URL: /namespaces/{namespace}/tenants/{tenant}/log
|
|
summary: Get Tenant Logs
|
|
HTTP Verb:GET
|
|
*/
|
|
request, err := http.NewRequest(
|
|
"GET",
|
|
"http://localhost:9090/api/v1/namespaces/"+nameSpace+"/tenants/"+tenant+"/log",
|
|
nil,
|
|
)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func SetTenantLogs(labels, annotations, nodeSelector, dbLabels, dbAnnotations, dbNodeSelector []string, nameSpace, tenant, dbServiceAccountName, logMemRequest, logDBMemRequest, diskCapacityGB, serviceAccountName string) (*http.Response, error) {
|
|
/*
|
|
URL: /namespaces/{namespace}/tenants/{tenant}/log
|
|
summary: Set Tenant Logs
|
|
HTTP Verb: PUT
|
|
*/
|
|
requestDataAdd := map[string]interface{}{
|
|
"labels": labels,
|
|
"annotations": annotations,
|
|
"dbAnnotations": dbAnnotations,
|
|
"dbLabels": dbLabels,
|
|
"dbNodeSelector": dbNodeSelector,
|
|
"diskCapacityGB": diskCapacityGB,
|
|
"nodeSelector": nodeSelector,
|
|
"serviceAccountName": serviceAccountName,
|
|
"dbServiceAccountName": dbServiceAccountName,
|
|
"logMemRequest": logMemRequest,
|
|
"logDBMemRequest": logDBMemRequest,
|
|
}
|
|
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
|
requestDataBody := bytes.NewReader(requestDataJSON)
|
|
request, err := http.NewRequest(
|
|
"PUT",
|
|
"http://localhost:9090/api/v1/namespaces/"+nameSpace+"/tenants/"+tenant+"/log",
|
|
requestDataBody,
|
|
)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func TestGetTenantLogs(t *testing.T) {
|
|
// Vars
|
|
assert := assert.New(t)
|
|
namespace := "tenant-lite"
|
|
tenant := "storage-lite"
|
|
|
|
// Get Log Settings
|
|
resp, err := GetTenantLogs(namespace, tenant)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200,
|
|
resp.StatusCode,
|
|
inspectHTTPResponse(resp),
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestSetTenantLogs(t *testing.T) {
|
|
// Vars
|
|
assert := assert.New(t)
|
|
nameSpace := "tenant-lite"
|
|
tenant := "storage-lite"
|
|
var nodeSelector []string
|
|
var labels []string
|
|
var annotations []string
|
|
var dbAnnotations []string
|
|
var dbNodeSelector []string
|
|
var dbLabels []string
|
|
diskCapacityGB := "2"
|
|
dbServiceAccountName := ""
|
|
logMemRequest := "0Gi"
|
|
logDBMemRequest := "0Gi"
|
|
serviceAccountName := ""
|
|
|
|
// Set Tenant Logs
|
|
resp, err := SetTenantLogs(
|
|
labels,
|
|
annotations,
|
|
nodeSelector,
|
|
dbLabels,
|
|
dbAnnotations,
|
|
dbNodeSelector,
|
|
nameSpace,
|
|
tenant,
|
|
dbServiceAccountName,
|
|
logMemRequest,
|
|
logDBMemRequest,
|
|
diskCapacityGB,
|
|
serviceAccountName,
|
|
)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200,
|
|
resp.StatusCode,
|
|
inspectHTTPResponse(resp),
|
|
)
|
|
}
|
|
}
|
|
|
|
func TenantDetails(nameSpace, tenant string) (*http.Response, error) {
|
|
/*
|
|
url: /namespaces/{namespace}/tenants/{tenant}
|
|
summary: Tenant Details
|
|
operationId: TenantDetails
|
|
HTTP Verb: GET
|
|
*/
|
|
request, err := http.NewRequest(
|
|
"GET",
|
|
"http://localhost:9090/api/v1/namespaces/"+nameSpace+"/tenants/"+tenant,
|
|
nil,
|
|
)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
|
request.Header.Add("Content-Type", "application/json")
|
|
client := &http.Client{
|
|
Timeout: 2 * time.Second,
|
|
}
|
|
response, err := client.Do(request)
|
|
return response, err
|
|
}
|
|
|
|
func TestTenantDetails(t *testing.T) {
|
|
// Vars
|
|
assert := assert.New(t)
|
|
nameSpace := "tenant-lite"
|
|
tenant := "storage-lite"
|
|
resp, err := TenantDetails(nameSpace, tenant)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return
|
|
}
|
|
if resp != nil {
|
|
assert.Equal(
|
|
200,
|
|
resp.StatusCode,
|
|
inspectHTTPResponse(resp),
|
|
)
|
|
}
|
|
}
|