Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c741e9ccae | ||
|
|
ab835286b0 | ||
|
|
6485718a97 | ||
|
|
00bcb54b67 | ||
|
|
fef7863810 | ||
|
|
427b9b4892 | ||
|
|
34adc5451d | ||
|
|
224e8d4bba | ||
|
|
07d75e19d5 | ||
|
|
31871f54d4 | ||
|
|
6069991405 | ||
|
|
a017c71d20 | ||
|
|
4001f14953 | ||
|
|
130413cbef | ||
|
|
0622cc658b | ||
|
|
663a5b196d | ||
|
|
6f676f73a4 | ||
|
|
26d7001ae1 | ||
|
|
f79a8e8177 | ||
|
|
0e5147bb1d | ||
|
|
566fb27fc1 | ||
|
|
8c18829089 | ||
|
|
c68c175827 | ||
|
|
415088ae2d | ||
|
|
158926f192 | ||
|
|
f026ffffc8 | ||
|
|
bd0edea3df | ||
|
|
07b4dad4d3 | ||
|
|
0df796bc03 | ||
|
|
cf0212391e | ||
|
|
cb3a695c25 |
2
.github/workflows/deploy-tenant.sh
vendored
2
.github/workflows/deploy-tenant.sh
vendored
@@ -65,7 +65,7 @@ function main() {
|
||||
|
||||
check_tenant_status tenant-lite storage-lite
|
||||
|
||||
kubectl -n minio-operator port-forward svc/console 9090 &
|
||||
kubectl proxy &
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
2
.github/workflows/jobs.yaml
vendored
2
.github/workflows/jobs.yaml
vendored
@@ -1346,7 +1346,7 @@ jobs:
|
||||
result=${result%\%}
|
||||
echo "result:"
|
||||
echo $result
|
||||
threshold=36.6
|
||||
threshold=41.2
|
||||
if (( $(echo "$result >= $threshold" |bc -l) )); then
|
||||
echo "It is equal or greater than threshold, passed!"
|
||||
else
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -19,6 +19,7 @@ vendor/
|
||||
|
||||
# Ignore executables
|
||||
target/
|
||||
!pkg/logger/target/
|
||||
console
|
||||
!console/
|
||||
|
||||
|
||||
10
Makefile
10
Makefile
@@ -164,18 +164,12 @@ test-sso-integration:
|
||||
-p 9000:9000 \
|
||||
-p 9001:9001 \
|
||||
-e MINIO_IDENTITY_OPENID_CLIENT_SECRET=0nfJuqIt0iPnRIUJkvetve5l38C6gi9W \
|
||||
-e MINIO_IDENTITY_OPENID_CONFIG_URL=http://keycloak-container:8080/auth/realms/myrealm/.well-known/openid-configuration \
|
||||
-e MINIO_IDENTITY_OPENID_CLIENT_ID="account" \
|
||||
-e MINIO_ROOT_USER=minio \
|
||||
-e MINIO_ROOT_PASSWORD=minio123 $(MINIO_VERSION) server /data{1...4} --address :9000 --console-address :9001)
|
||||
@(sleep 60)
|
||||
@echo "run mc commands"
|
||||
@(docker run --name minio-client --network my-net -dit --entrypoint=/bin/sh minio/mc)
|
||||
@(docker exec minio-client mc alias set myminio/ http://minio:9000 minio minio123)
|
||||
@(docker exec minio-client mc admin config set myminio identity_openid config_url="http://keycloak-container:8080/auth/realms/myrealm/.well-known/openid-configuration" client_id="account")
|
||||
@(docker exec minio-client mc admin service restart myminio)
|
||||
@echo "starting bash script"
|
||||
@(env bash $(PWD)/sso-integration/set-sso.sh)
|
||||
@echo "install jq"
|
||||
@(sudo apt install jq)
|
||||
@echo "Executing the test:"
|
||||
@(cd sso-integration && go test -coverpkg=../restapi -c -tags testrunmain . && mkdir -p coverage && ./sso-integration.test -test.v -test.run "^Test*" -test.coverprofile=coverage/sso-system.out)
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/minio/pkg/env"
|
||||
@@ -68,7 +70,7 @@ func GetMinioImage() (*string, error) {
|
||||
return &image, nil
|
||||
}
|
||||
latestMinIOImage, errLatestMinIOImage := utils.GetLatestMinIOImage(
|
||||
&utils.HTTPClient{
|
||||
&xhttp.Client{
|
||||
Client: &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
|
||||
@@ -20,10 +20,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/logger"
|
||||
|
||||
"github.com/minio/cli"
|
||||
"github.com/minio/console/restapi"
|
||||
)
|
||||
@@ -36,15 +40,28 @@ var appCmds = []cli.Command{
|
||||
|
||||
// StartServer starts the console service
|
||||
func StartServer(ctx *cli.Context) error {
|
||||
if os.Getenv("CONSOLE_OPERATOR_MODE") != "" && os.Getenv("CONSOLE_OPERATOR_MODE") == "on" {
|
||||
return startOperatorServer(ctx)
|
||||
}
|
||||
|
||||
// Load all certificates
|
||||
if err := loadAllCerts(ctx); err != nil {
|
||||
// Log this as a warning and continue running console without TLS certificates
|
||||
restapi.LogError("Unable to load certs: %v", err)
|
||||
}
|
||||
|
||||
xctx := context.Background()
|
||||
transport := restapi.PrepareSTSClientTransport(false)
|
||||
if err := logger.InitializeLogger(xctx, transport); err != nil {
|
||||
fmt.Println("error InitializeLogger", err)
|
||||
logger.CriticalIf(xctx, err)
|
||||
}
|
||||
// custom error configuration
|
||||
restapi.LogInfo = logger.Info
|
||||
restapi.LogError = logger.Error
|
||||
restapi.LogIf = logger.LogIf
|
||||
|
||||
if os.Getenv("CONSOLE_OPERATOR_MODE") != "" && os.Getenv("CONSOLE_OPERATOR_MODE") == "on" {
|
||||
return startOperatorServer(ctx)
|
||||
}
|
||||
|
||||
var rctx restapi.Context
|
||||
if err := rctx.Load(ctx); err != nil {
|
||||
restapi.LogError("argument validation failed: %v", err)
|
||||
|
||||
@@ -20,9 +20,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/logger"
|
||||
|
||||
"github.com/minio/cli"
|
||||
"github.com/minio/console/restapi"
|
||||
)
|
||||
@@ -39,6 +43,17 @@ func StartServer(ctx *cli.Context) error {
|
||||
restapi.LogError("Unable to load certs: %v", err)
|
||||
}
|
||||
|
||||
xctx := context.Background()
|
||||
transport := restapi.PrepareSTSClientTransport(false)
|
||||
if err := logger.InitializeLogger(xctx, transport); err != nil {
|
||||
fmt.Println("error InitializeLogger", err)
|
||||
logger.CriticalIf(xctx, err)
|
||||
}
|
||||
// custom error configuration
|
||||
restapi.LogInfo = logger.Info
|
||||
restapi.LogError = logger.Error
|
||||
restapi.LogIf = logger.LogIf
|
||||
|
||||
var rctx restapi.Context
|
||||
if err := rctx.Load(ctx); err != nil {
|
||||
restapi.LogError("argument validation failed: %v", err)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// +build operator
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
// Copyright (c) 2022 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
|
||||
@@ -20,6 +20,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
@@ -27,6 +28,8 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/logger"
|
||||
|
||||
"github.com/minio/console/restapi"
|
||||
|
||||
"github.com/go-openapi/loads"
|
||||
@@ -106,7 +109,7 @@ func buildOperatorServer() (*operatorapi.Server, error) {
|
||||
}
|
||||
|
||||
api := operations.NewOperatorAPI(swaggerSpec)
|
||||
api.Logger = operatorapi.LogInfo
|
||||
api.Logger = restapi.LogInfo
|
||||
server := operatorapi.NewServer(api)
|
||||
|
||||
parser := flags.NewParser(server, flags.Default)
|
||||
@@ -147,7 +150,7 @@ func loadOperatorAllCerts(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
// load the certificates and the CAs
|
||||
operatorapi.GlobalRootCAs, operatorapi.GlobalPublicCerts, operatorapi.GlobalTLSCertsManager, err = certs.GetAllCertificatesAndCAs()
|
||||
restapi.GlobalRootCAs, restapi.GlobalPublicCerts, restapi.GlobalTLSCertsManager, err = certs.GetAllCertificatesAndCAs()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load certificates at %s: failed with %w", certs.GlobalCertsDir.Get(), err)
|
||||
}
|
||||
@@ -159,12 +162,12 @@ func loadOperatorAllCerts(ctx *cli.Context) error {
|
||||
swaggerServerCACertificate := ctx.String("tls-ca")
|
||||
// load tls cert and key from swagger server tls-certificate and tls-key flags
|
||||
if swaggerServerCertificate != "" && swaggerServerCertificateKey != "" {
|
||||
if err = operatorapi.GlobalTLSCertsManager.AddCertificate(swaggerServerCertificate, swaggerServerCertificateKey); err != nil {
|
||||
if err = restapi.GlobalTLSCertsManager.AddCertificate(swaggerServerCertificate, swaggerServerCertificateKey); err != nil {
|
||||
return err
|
||||
}
|
||||
x509Certs, err := certs.ParsePublicCertFile(swaggerServerCertificate)
|
||||
if err == nil {
|
||||
operatorapi.GlobalPublicCerts = append(operatorapi.GlobalPublicCerts, x509Certs...)
|
||||
restapi.GlobalPublicCerts = append(restapi.GlobalPublicCerts, x509Certs...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +175,7 @@ func loadOperatorAllCerts(ctx *cli.Context) error {
|
||||
if swaggerServerCACertificate != "" {
|
||||
caCert, caCertErr := ioutil.ReadFile(swaggerServerCACertificate)
|
||||
if caCertErr == nil {
|
||||
operatorapi.GlobalRootCAs.AppendCertsFromPEM(caCert)
|
||||
restapi.GlobalRootCAs.AppendCertsFromPEM(caCert)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,20 +189,32 @@ func loadOperatorAllCerts(ctx *cli.Context) error {
|
||||
|
||||
// StartServer starts the console service
|
||||
func startOperatorServer(ctx *cli.Context) error {
|
||||
if err := loadOperatorAllCerts(ctx); err != nil {
|
||||
|
||||
if err := loadAllCerts(ctx); err != nil {
|
||||
// Log this as a warning and continue running console without TLS certificates
|
||||
operatorapi.LogError("Unable to load certs: %v", err)
|
||||
restapi.LogError("Unable to load certs: %v", err)
|
||||
}
|
||||
|
||||
xctx := context.Background()
|
||||
transport := restapi.PrepareSTSClientTransport(false)
|
||||
if err := logger.InitializeLogger(xctx, transport); err != nil {
|
||||
fmt.Println("error InitializeLogger", err)
|
||||
logger.CriticalIf(xctx, err)
|
||||
}
|
||||
// custom error configuration
|
||||
restapi.LogInfo = logger.Info
|
||||
restapi.LogError = logger.Error
|
||||
restapi.LogIf = logger.LogIf
|
||||
|
||||
var rctx operatorapi.Context
|
||||
if err := rctx.Load(ctx); err != nil {
|
||||
operatorapi.LogError("argument validation failed: %v", err)
|
||||
restapi.LogError("argument validation failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
server, err := buildOperatorServer()
|
||||
if err != nil {
|
||||
operatorapi.LogError("Unable to initialize console server: %v", err)
|
||||
restapi.LogError("Unable to initialize console server: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -212,7 +227,7 @@ func startOperatorServer(ctx *cli.Context) error {
|
||||
operatorapi.Port = strconv.Itoa(server.Port)
|
||||
operatorapi.Hostname = server.Host
|
||||
|
||||
if len(operatorapi.GlobalPublicCerts) > 0 {
|
||||
if len(restapi.GlobalPublicCerts) > 0 {
|
||||
// If TLS certificates are provided enforce the HTTPS schema, meaning console will redirect
|
||||
// plain HTTP connections to HTTPS server
|
||||
server.EnabledListeners = []string{"http", "https"}
|
||||
|
||||
9
go.mod
9
go.mod
@@ -6,6 +6,7 @@ require (
|
||||
github.com/blang/semver/v4 v4.0.0
|
||||
github.com/cheggaaa/pb/v3 v3.0.8
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/go-openapi/errors v0.20.2
|
||||
github.com/go-openapi/loads v0.21.1
|
||||
github.com/go-openapi/runtime v0.23.3
|
||||
@@ -14,10 +15,12 @@ require (
|
||||
github.com/go-openapi/swag v0.21.1
|
||||
github.com/go-openapi/validate v0.21.0
|
||||
github.com/golang-jwt/jwt/v4 v4.4.1
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/jessevdk/go-flags v1.5.0
|
||||
github.com/klauspost/compress v1.15.1
|
||||
github.com/minio/cli v1.22.0
|
||||
github.com/minio/highwayhash v1.0.2
|
||||
github.com/minio/kes v0.19.2
|
||||
github.com/minio/madmin-go v1.3.12
|
||||
github.com/minio/mc v0.0.0-20220419155441-cc4ff3a0cc82
|
||||
@@ -57,7 +60,6 @@ require (
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/gdamore/encoding v1.0.0 // indirect
|
||||
github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 // indirect
|
||||
@@ -74,7 +76,6 @@ require (
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
@@ -109,7 +110,7 @@ require (
|
||||
github.com/navidys/tvxwidgets v0.1.0 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/philhofer/fwd v1.1.1 // indirect
|
||||
github.com/philhofer/fwd v1.1.2-0.20210722190033-5c56ac6d0bb9 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/xattr v0.4.5 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
@@ -126,7 +127,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tinylib/msgp v1.1.6 // indirect
|
||||
github.com/tinylib/msgp v1.1.7-0.20211026165309-e818a1881b0e // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
|
||||
83
integration/config_test.go
Normal file
83
integration/config_test.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_ConfigAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
type args struct {
|
||||
api string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "Config - Valid",
|
||||
args: args{
|
||||
api: "/configs",
|
||||
},
|
||||
expectedStatus: 200,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
requestDataPolicy := map[string]interface{}{}
|
||||
|
||||
requestDataJSON, _ := json.Marshal(requestDataPolicy)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"GET", fmt.Sprintf("http://localhost:9090/api/v1%s", tt.args.api), requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, tt.name+" Failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
365
integration/groups_test.go
Normal file
365
integration/groups_test.go
Normal file
@@ -0,0 +1,365 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_AddGroupAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
AddUser("member1", "testtest", []string{}, []string{"consoleAdmin"})
|
||||
|
||||
type args struct {
|
||||
group string
|
||||
members []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "Create Group - Valid",
|
||||
args: args{
|
||||
group: "test",
|
||||
members: []string{"member1"},
|
||||
},
|
||||
expectedStatus: 201,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Create Group - Invalid",
|
||||
args: args{
|
||||
group: "test",
|
||||
members: []string{},
|
||||
},
|
||||
expectedStatus: 400,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
requestDataPolicy := map[string]interface{}{}
|
||||
requestDataPolicy["group"] = tt.args.group
|
||||
requestDataPolicy["members"] = tt.args.members
|
||||
|
||||
requestDataJSON, _ := json.Marshal(requestDataPolicy)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"POST", "http://localhost:9090/api/v1/groups", requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_GetGroupAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
AddUser("member2", "testtest", []string{}, []string{"consoleAdmin"})
|
||||
AddGroup("getgroup1", []string{"member2"})
|
||||
|
||||
type args struct {
|
||||
api string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "Get Group - Valid",
|
||||
args: args{
|
||||
api: "?name=getgroup1",
|
||||
},
|
||||
expectedStatus: 200,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Get Group - Invalid",
|
||||
args: args{
|
||||
api: "?name=askfjalkd",
|
||||
},
|
||||
expectedStatus: 500,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
requestDataPolicy := map[string]interface{}{}
|
||||
|
||||
requestDataJSON, _ := json.Marshal(requestDataPolicy)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"GET", fmt.Sprintf("http://localhost:9090/api/v1/group%s", tt.args.api), requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_ListGroupsAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "Get Group - Valid",
|
||||
expectedStatus: 200,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
requestDataPolicy := map[string]interface{}{}
|
||||
|
||||
requestDataJSON, _ := json.Marshal(requestDataPolicy)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"GET", "http://localhost:9090/api/v1/groups", requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_PutGroupsAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
AddUser("member3", "testtest", []string{}, []string{"consoleAdmin"})
|
||||
AddGroup("putgroup1", []string{})
|
||||
|
||||
type args struct {
|
||||
api string
|
||||
members []string
|
||||
status string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "Put Group - Valid",
|
||||
args: args{
|
||||
api: "?name=putgroup1",
|
||||
members: []string{"member3"},
|
||||
status: "enabled",
|
||||
},
|
||||
expectedStatus: 200,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Put Group - Invalid",
|
||||
args: args{
|
||||
api: "?name=gdgfdfgd",
|
||||
members: []string{"member3"},
|
||||
status: "enabled",
|
||||
},
|
||||
expectedStatus: 500,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
requestDataPolicy := map[string]interface{}{}
|
||||
|
||||
requestDataPolicy["members"] = tt.args.members
|
||||
requestDataPolicy["status"] = tt.args.status
|
||||
|
||||
requestDataJSON, _ := json.Marshal(requestDataPolicy)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"PUT", fmt.Sprintf("http://localhost:9090/api/v1/group%s", tt.args.api), requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_DeleteGroupAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
AddGroup("grouptests1", []string{})
|
||||
|
||||
type args struct {
|
||||
api string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
verb string
|
||||
}{
|
||||
{
|
||||
name: "Delete Group - Valid",
|
||||
args: args{
|
||||
api: "?name=grouptests1",
|
||||
},
|
||||
verb: "DELETE",
|
||||
expectedStatus: 204,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Delete Group - Invalid",
|
||||
args: args{
|
||||
api: "?name=grouptests12345",
|
||||
},
|
||||
verb: "DELETE",
|
||||
expectedStatus: 404,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Access Group After Delete - Invalid",
|
||||
args: args{
|
||||
api: "?name=grouptests1",
|
||||
},
|
||||
verb: "GET",
|
||||
expectedStatus: 500,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
requestDataPolicy := map[string]interface{}{}
|
||||
|
||||
requestDataJSON, _ := json.Marshal(requestDataPolicy)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
tt.verb, fmt.Sprintf("http://localhost:9090/api/v1/group%s", tt.args.api), requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"testing"
|
||||
@@ -30,7 +31,64 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_PolicyAPI(t *testing.T) {
|
||||
func AddPolicy(name string, definition string) (*http.Response, error) {
|
||||
/*
|
||||
This is an atomic function to add user and can be reused across
|
||||
different functions.
|
||||
*/
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"name": name,
|
||||
"policy": definition,
|
||||
}
|
||||
|
||||
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"POST", "http://localhost:9090/api/v1/policies", requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func SetPolicy(policies []string, entityName string, entityType string) (*http.Response, error) {
|
||||
/*
|
||||
This is an atomic function to add user and can be reused across
|
||||
different functions.
|
||||
*/
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"name": policies,
|
||||
"entityType": entityType,
|
||||
"entityName": entityName,
|
||||
}
|
||||
|
||||
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"PUT", "http://localhost:9090/api/v1/set-policy", requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func Test_AddPolicyAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
type args struct {
|
||||
@@ -69,11 +127,12 @@ func Test_PolicyAPI(t *testing.T) {
|
||||
expectedStatus: 201,
|
||||
expectedError: nil,
|
||||
},
|
||||
|
||||
{
|
||||
name: "Create Policy - Invalid",
|
||||
args: args{
|
||||
api: "/policies",
|
||||
name: "test",
|
||||
name: "test2",
|
||||
policy: swag.String(`
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
@@ -94,6 +153,31 @@ func Test_PolicyAPI(t *testing.T) {
|
||||
expectedStatus: 500,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Create Policy - Space in Name",
|
||||
args: args{
|
||||
api: "/policies",
|
||||
name: "space test",
|
||||
policy: swag.String(`
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:GetBucketLocation",
|
||||
"s3:GetObject"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`),
|
||||
},
|
||||
expectedStatus: 400,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -103,11 +187,9 @@ func Test_PolicyAPI(t *testing.T) {
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
// Add policy
|
||||
|
||||
requestDataPolicy := map[string]interface{}{}
|
||||
requestDataPolicy["name"] = tt.args.name
|
||||
if tt.args.policy != nil {
|
||||
requestDataPolicy["name"] = tt.args.name
|
||||
requestDataPolicy["policy"] = *tt.args.policy
|
||||
}
|
||||
|
||||
@@ -127,10 +209,604 @@ func Test_PolicyAPI(t *testing.T) {
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, "Status Code is incorrect")
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, tt.name+" Failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_SetPolicyAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
AddUser("policyuser1", "testtest", []string{}, []string{"readwrite"})
|
||||
AddGroup("testgroup123", []string{})
|
||||
AddPolicy("setpolicytest", `
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:GetBucketLocation",
|
||||
"s3:GetObject"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`)
|
||||
|
||||
type args struct {
|
||||
api string
|
||||
entityType string
|
||||
entityName string
|
||||
policyName []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "Set Policy - Valid",
|
||||
args: args{
|
||||
api: "/set-policy",
|
||||
policyName: []string{"setpolicytest"},
|
||||
entityType: "user",
|
||||
entityName: "policyuser1",
|
||||
},
|
||||
expectedStatus: 204,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Set Policy - Invalid",
|
||||
args: args{
|
||||
api: "/set-policy",
|
||||
policyName: []string{"test3"},
|
||||
entityType: "user",
|
||||
entityName: "policyuser1",
|
||||
},
|
||||
expectedStatus: 500,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Set Policy Group - Valid",
|
||||
args: args{
|
||||
api: "/set-policy",
|
||||
policyName: []string{"setpolicytest"},
|
||||
entityType: "group",
|
||||
entityName: "testgroup123",
|
||||
},
|
||||
expectedStatus: 204,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Set Policy Group - Invalid",
|
||||
args: args{
|
||||
api: "/set-policy",
|
||||
policyName: []string{"test3"},
|
||||
entityType: "group",
|
||||
entityName: "testgroup123",
|
||||
},
|
||||
expectedStatus: 500,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
requestDataPolicy := map[string]interface{}{}
|
||||
requestDataPolicy["entityName"] = tt.args.entityName
|
||||
requestDataPolicy["entityType"] = tt.args.entityType
|
||||
if tt.args.policyName != nil {
|
||||
requestDataPolicy["name"] = tt.args.policyName
|
||||
}
|
||||
|
||||
requestDataJSON, _ := json.Marshal(requestDataPolicy)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"PUT", fmt.Sprintf("http://localhost:9090/api/v1%s", tt.args.api), requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, tt.name+" Failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_SetPolicyMultipleAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
AddUser("policyuser2", "testtest", []string{}, []string{"readwrite"})
|
||||
AddUser("policyuser3", "testtest", []string{}, []string{"readwrite"})
|
||||
AddGroup("testgroup1234", []string{})
|
||||
AddPolicy("setpolicytest2", `
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:GetBucketLocation",
|
||||
"s3:GetObject"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`)
|
||||
|
||||
type args struct {
|
||||
api string
|
||||
users []string
|
||||
groups []string
|
||||
name []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "Set Policy - Valid",
|
||||
args: args{
|
||||
api: "/set-policy-multi",
|
||||
name: []string{"setpolicytest2"},
|
||||
users: []string{"policyuser2", "policyuser3"},
|
||||
},
|
||||
expectedStatus: 204,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Set Policy - Invalid",
|
||||
args: args{
|
||||
api: "/set-policy-multi",
|
||||
name: []string{"test3"},
|
||||
users: []string{"policyuser2", "policyuser3"},
|
||||
},
|
||||
expectedStatus: 500,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Set Policy Group - Valid",
|
||||
args: args{
|
||||
api: "/set-policy-multi",
|
||||
name: []string{"setpolicytest2"},
|
||||
groups: []string{"testgroup1234"},
|
||||
},
|
||||
expectedStatus: 204,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Set Policy Group - Valid",
|
||||
args: args{
|
||||
api: "/set-policy-multi",
|
||||
name: []string{"setpolicytest23"},
|
||||
groups: []string{"testgroup1234"},
|
||||
},
|
||||
expectedStatus: 500,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
requestDataPolicy := map[string]interface{}{}
|
||||
requestDataPolicy["name"] = tt.args.name
|
||||
requestDataPolicy["users"] = tt.args.users
|
||||
requestDataPolicy["groups"] = tt.args.groups
|
||||
|
||||
requestDataJSON, _ := json.Marshal(requestDataPolicy)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"PUT", fmt.Sprintf("http://localhost:9090/api/v1%s", tt.args.api), requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, tt.name+" Failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_ListPoliciesAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
type args struct {
|
||||
api string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "List Policies",
|
||||
args: args{
|
||||
api: "/policies",
|
||||
},
|
||||
expectedStatus: 200,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(
|
||||
"GET", fmt.Sprintf("http://localhost:9090/api/v1%s", tt.args.api), nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, tt.name+" Failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_GetPolicyAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
AddPolicy("getpolicytest", `
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:GetBucketLocation",
|
||||
"s3:GetObject"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`)
|
||||
|
||||
type args struct {
|
||||
api string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "Get Policies - Invalid",
|
||||
args: args{
|
||||
api: "/policy?name=test3",
|
||||
},
|
||||
expectedStatus: 500,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Get Policies - Valid",
|
||||
args: args{
|
||||
api: "/policy?name=getpolicytest",
|
||||
},
|
||||
expectedStatus: 200,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(
|
||||
"GET", fmt.Sprintf("http://localhost:9090/api/v1%s", tt.args.api), nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, tt.name+" Failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_PolicyListUsersAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
AddUser("policyuser4", "testtest", []string{}, []string{"readwrite"})
|
||||
AddPolicy("policylistusers", `
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:GetBucketLocation",
|
||||
"s3:GetObject"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`)
|
||||
SetPolicy([]string{"policylistusers"}, "policyuser4", "user")
|
||||
|
||||
type args struct {
|
||||
api string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "List Users for Policy - Valid",
|
||||
args: args{
|
||||
api: "/policies/policylistusers/users",
|
||||
},
|
||||
expectedStatus: 200,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "List Users for Policy - Invalid",
|
||||
args: args{
|
||||
api: "/policies/test2/users",
|
||||
},
|
||||
expectedStatus: 404,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(
|
||||
"GET", fmt.Sprintf("http://localhost:9090/api/v1%s", tt.args.api), nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
bodyBytes, _ := ioutil.ReadAll(response.Body)
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, tt.name+" Failed")
|
||||
if response.StatusCode == 200 {
|
||||
assert.Equal("[\"policyuser4\"]\n", string(bodyBytes))
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_PolicyListGroupsAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
AddGroup("testgroup12345", []string{})
|
||||
AddPolicy("policylistgroups", `
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:GetBucketLocation",
|
||||
"s3:GetObject"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`)
|
||||
SetPolicy([]string{"policylistgroups"}, "testgroup12345", "group")
|
||||
|
||||
type args struct {
|
||||
api string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "List Users for Policy - Valid",
|
||||
args: args{
|
||||
api: "/policies/policylistgroups/groups",
|
||||
},
|
||||
expectedStatus: 200,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "List Users for Policy - Invalid",
|
||||
args: args{
|
||||
api: "/policies/test3/groups",
|
||||
},
|
||||
expectedStatus: 404,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(
|
||||
"GET", fmt.Sprintf("http://localhost:9090/api/v1%s", tt.args.api), nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
bodyBytes, _ := ioutil.ReadAll(response.Body)
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, tt.name+" Failed")
|
||||
if response.StatusCode == 200 {
|
||||
assert.Equal("[\"testgroup12345\"]\n", string(bodyBytes))
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_DeletePolicyAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
AddPolicy("testdelete", `
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:GetBucketLocation",
|
||||
"s3:GetObject"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`)
|
||||
type args struct {
|
||||
api string
|
||||
method string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "Delete Policies - Valid",
|
||||
args: args{
|
||||
api: "/policy?name=testdelete",
|
||||
method: "DELETE",
|
||||
},
|
||||
expectedStatus: 204,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Get Policy After Delete - Invalid",
|
||||
args: args{
|
||||
api: "/policy?name=testdelete",
|
||||
method: "GET",
|
||||
},
|
||||
expectedStatus: 500,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(
|
||||
tt.args.method, fmt.Sprintf("http://localhost:9090/api/v1%s", tt.args.api), nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, tt.name+" Failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
121
integration/profiling_test.go
Normal file
121
integration/profiling_test.go
Normal file
@@ -0,0 +1,121 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 integration
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStartProfiling(t *testing.T) {
|
||||
testAsser := assert.New(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
}{
|
||||
{
|
||||
name: "start/stop profiling",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
files := map[string]bool{
|
||||
"profile-127.0.0.1:9000-goroutines.txt": false,
|
||||
"profile-127.0.0.1:9000-goroutines-before.txt": false,
|
||||
"profile-127.0.0.1:9000-goroutines-before,debug=2.txt": false,
|
||||
"profile-127.0.0.1:9000-threads-before.pprof": false,
|
||||
"profile-127.0.0.1:9000-mem.pprof": false,
|
||||
"profile-127.0.0.1:9000-threads.pprof": false,
|
||||
"profile-127.0.0.1:9000-cpu.pprof": false,
|
||||
"profile-127.0.0.1:9000-mem-before.pprof": false,
|
||||
"profile-127.0.0.1:9000-block.pprof": false,
|
||||
"profile-127.0.0.1:9000-trace.trace": false,
|
||||
"profile-127.0.0.1:9000-mutex.pprof": false,
|
||||
"profile-127.0.0.1:9000-mutex-before.pprof": false,
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
destination := "/api/v1/profiling/start"
|
||||
finalURL := fmt.Sprintf("http://localhost:9090%s", destination)
|
||||
request, err := http.NewRequest("POST", finalURL, strings.NewReader("{\"type\":\"cpu,mem,block,mutex,trace,threads,goroutines\"}"))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
|
||||
response, err := client.Do(request)
|
||||
|
||||
testAsser.Nil(err, fmt.Sprintf("%s returned an error: %v", tt.name, err))
|
||||
testAsser.Equal(201, response.StatusCode)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
destination = "/api/v1/profiling/stop"
|
||||
finalURL = fmt.Sprintf("http://localhost:9090%s", destination)
|
||||
request, err = http.NewRequest("POST", finalURL, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
|
||||
response, err = client.Do(request)
|
||||
|
||||
testAsser.Nil(err, fmt.Sprintf("%s returned an error: %v", tt.name, err))
|
||||
testAsser.Equal(200, response.StatusCode)
|
||||
zipFileBytes, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
testAsser.Nil(err, fmt.Sprintf("%s returned an error: %v", tt.name, err))
|
||||
}
|
||||
filetype := http.DetectContentType(zipFileBytes)
|
||||
testAsser.Equal("application/zip", filetype)
|
||||
|
||||
zipReader, err := zip.NewReader(bytes.NewReader(zipFileBytes), int64(len(zipFileBytes)))
|
||||
if err != nil {
|
||||
testAsser.Nil(err, fmt.Sprintf("%s returned an error: %v", tt.name, err))
|
||||
}
|
||||
|
||||
// Read all the files from zip archive
|
||||
for _, zipFile := range zipReader.File {
|
||||
files[zipFile.Name] = true
|
||||
}
|
||||
|
||||
for k, v := range files {
|
||||
testAsser.Equal(true, v, fmt.Sprintf("%s : compressed file expected to have %v file inside", tt.name, k))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -854,3 +854,68 @@ func TestUsersGroupsBulk(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_GetUserPolicyAPI(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// 1. Create an active user with valid policy
|
||||
var groups = []string{}
|
||||
var policies = []string{"readwrite"}
|
||||
addUserResponse, addUserError := AddUser(
|
||||
"getpolicyuser", "secretKey", groups, policies)
|
||||
if addUserError != nil {
|
||||
log.Println(addUserError)
|
||||
return
|
||||
}
|
||||
if addUserResponse != nil {
|
||||
fmt.Println("StatusCode:", addUserResponse.StatusCode)
|
||||
assert.Equal(
|
||||
201, addUserResponse.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
type args struct {
|
||||
api string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedStatus int
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "Get User Policies",
|
||||
args: args{
|
||||
api: "/user/policy",
|
||||
},
|
||||
expectedStatus: 200,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(
|
||||
"GET", fmt.Sprintf("http://localhost:9090/api/v1%s", tt.args.api), nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
assert.Equal(tt.expectedStatus, response.StatusCode, tt.name+" Failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
serviceAccountName: console-sa
|
||||
containers:
|
||||
- name: console
|
||||
image: 'minio/console:v0.16.0'
|
||||
image: 'minio/console:v0.16.2'
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
env:
|
||||
- name: CONSOLE_OPERATOR_MODE
|
||||
|
||||
@@ -32,7 +32,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: console
|
||||
image: 'minio/console:v0.16.0'
|
||||
image: 'minio/console:v0.16.2'
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
env:
|
||||
- name: CONSOLE_MINIO_SERVER
|
||||
|
||||
70
models/condition.go
Normal file
70
models/condition.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// Condition condition
|
||||
//
|
||||
// swagger:model condition
|
||||
type Condition struct {
|
||||
|
||||
// status
|
||||
Status string `json:"status,omitempty"`
|
||||
|
||||
// type
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this condition
|
||||
func (m *Condition) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this condition based on context it is used
|
||||
func (m *Condition) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *Condition) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *Condition) UnmarshalBinary(b []byte) error {
|
||||
var res Condition
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
70
models/config_map.go
Normal file
70
models/config_map.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// ConfigMap config map
|
||||
//
|
||||
// swagger:model configMap
|
||||
type ConfigMap struct {
|
||||
|
||||
// name
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// optional
|
||||
Optional bool `json:"optional,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this config map
|
||||
func (m *ConfigMap) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this config map based on context it is used
|
||||
func (m *ConfigMap) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *ConfigMap) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *ConfigMap) UnmarshalBinary(b []byte) error {
|
||||
var res ConfigMap
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
309
models/container.go
Normal file
309
models/container.go
Normal file
@@ -0,0 +1,309 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// Container container
|
||||
//
|
||||
// swagger:model container
|
||||
type Container struct {
|
||||
|
||||
// args
|
||||
Args []string `json:"args"`
|
||||
|
||||
// container ID
|
||||
ContainerID string `json:"containerID,omitempty"`
|
||||
|
||||
// environment variables
|
||||
EnvironmentVariables []*EnvironmentVariable `json:"environmentVariables"`
|
||||
|
||||
// host ports
|
||||
HostPorts []string `json:"hostPorts"`
|
||||
|
||||
// image
|
||||
Image string `json:"image,omitempty"`
|
||||
|
||||
// image ID
|
||||
ImageID string `json:"imageID,omitempty"`
|
||||
|
||||
// last state
|
||||
LastState *State `json:"lastState,omitempty"`
|
||||
|
||||
// mounts
|
||||
Mounts []*Mount `json:"mounts"`
|
||||
|
||||
// name
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// ports
|
||||
Ports []string `json:"ports"`
|
||||
|
||||
// ready
|
||||
Ready bool `json:"ready,omitempty"`
|
||||
|
||||
// restart count
|
||||
RestartCount int64 `json:"restartCount,omitempty"`
|
||||
|
||||
// state
|
||||
State *State `json:"state,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this container
|
||||
func (m *Container) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateEnvironmentVariables(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateLastState(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateMounts(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateState(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Container) validateEnvironmentVariables(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.EnvironmentVariables) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.EnvironmentVariables); i++ {
|
||||
if swag.IsZero(m.EnvironmentVariables[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.EnvironmentVariables[i] != nil {
|
||||
if err := m.EnvironmentVariables[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("environmentVariables" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("environmentVariables" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Container) validateLastState(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.LastState) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.LastState != nil {
|
||||
if err := m.LastState.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("lastState")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("lastState")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Container) validateMounts(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Mounts) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Mounts); i++ {
|
||||
if swag.IsZero(m.Mounts[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Mounts[i] != nil {
|
||||
if err := m.Mounts[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("mounts" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("mounts" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Container) validateState(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.State) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.State != nil {
|
||||
if err := m.State.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("state")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("state")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this container based on the context it is used
|
||||
func (m *Container) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateEnvironmentVariables(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateLastState(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateMounts(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateState(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Container) contextValidateEnvironmentVariables(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.EnvironmentVariables); i++ {
|
||||
|
||||
if m.EnvironmentVariables[i] != nil {
|
||||
if err := m.EnvironmentVariables[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("environmentVariables" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("environmentVariables" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Container) contextValidateLastState(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.LastState != nil {
|
||||
if err := m.LastState.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("lastState")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("lastState")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Container) contextValidateMounts(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.Mounts); i++ {
|
||||
|
||||
if m.Mounts[i] != nil {
|
||||
if err := m.Mounts[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("mounts" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("mounts" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Container) contextValidateState(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.State != nil {
|
||||
if err := m.State.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("state")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("state")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *Container) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *Container) UnmarshalBinary(b []byte) error {
|
||||
var res Container
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
517
models/describe_pod_wrapper.go
Normal file
517
models/describe_pod_wrapper.go
Normal file
@@ -0,0 +1,517 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// DescribePodWrapper describe pod wrapper
|
||||
//
|
||||
// swagger:model describePodWrapper
|
||||
type DescribePodWrapper struct {
|
||||
|
||||
// annotations
|
||||
Annotations []*Annotation `json:"annotations"`
|
||||
|
||||
// conditions
|
||||
Conditions []*Condition `json:"conditions"`
|
||||
|
||||
// containers
|
||||
Containers []*Container `json:"containers"`
|
||||
|
||||
// controller ref
|
||||
ControllerRef string `json:"controllerRef,omitempty"`
|
||||
|
||||
// deletion grace period seconds
|
||||
DeletionGracePeriodSeconds int64 `json:"deletionGracePeriodSeconds,omitempty"`
|
||||
|
||||
// deletion timestamp
|
||||
DeletionTimestamp string `json:"deletionTimestamp,omitempty"`
|
||||
|
||||
// labels
|
||||
Labels []*Label `json:"labels"`
|
||||
|
||||
// message
|
||||
Message string `json:"message,omitempty"`
|
||||
|
||||
// name
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// namespace
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
|
||||
// node name
|
||||
NodeName string `json:"nodeName,omitempty"`
|
||||
|
||||
// node selector
|
||||
NodeSelector []*NodeSelector `json:"nodeSelector"`
|
||||
|
||||
// phase
|
||||
Phase string `json:"phase,omitempty"`
|
||||
|
||||
// pod IP
|
||||
PodIP string `json:"podIP,omitempty"`
|
||||
|
||||
// priority
|
||||
Priority int64 `json:"priority,omitempty"`
|
||||
|
||||
// priority class name
|
||||
PriorityClassName string `json:"priorityClassName,omitempty"`
|
||||
|
||||
// qos class
|
||||
QosClass string `json:"qosClass,omitempty"`
|
||||
|
||||
// reason
|
||||
Reason string `json:"reason,omitempty"`
|
||||
|
||||
// start time
|
||||
StartTime string `json:"startTime,omitempty"`
|
||||
|
||||
// tolerations
|
||||
Tolerations []*Toleration `json:"tolerations"`
|
||||
|
||||
// volumes
|
||||
Volumes []*Volume `json:"volumes"`
|
||||
}
|
||||
|
||||
// Validate validates this describe pod wrapper
|
||||
func (m *DescribePodWrapper) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateAnnotations(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateConditions(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateContainers(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateLabels(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateNodeSelector(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateTolerations(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateVolumes(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) validateAnnotations(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Annotations) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Annotations); i++ {
|
||||
if swag.IsZero(m.Annotations[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Annotations[i] != nil {
|
||||
if err := m.Annotations[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("annotations" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("annotations" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) validateConditions(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Conditions) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Conditions); i++ {
|
||||
if swag.IsZero(m.Conditions[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Conditions[i] != nil {
|
||||
if err := m.Conditions[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("conditions" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("conditions" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) validateContainers(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Containers) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Containers); i++ {
|
||||
if swag.IsZero(m.Containers[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Containers[i] != nil {
|
||||
if err := m.Containers[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("containers" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("containers" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) validateLabels(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Labels) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Labels); i++ {
|
||||
if swag.IsZero(m.Labels[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Labels[i] != nil {
|
||||
if err := m.Labels[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("labels" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("labels" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) validateNodeSelector(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.NodeSelector) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.NodeSelector); i++ {
|
||||
if swag.IsZero(m.NodeSelector[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.NodeSelector[i] != nil {
|
||||
if err := m.NodeSelector[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("nodeSelector" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("nodeSelector" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) validateTolerations(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Tolerations) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Tolerations); i++ {
|
||||
if swag.IsZero(m.Tolerations[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Tolerations[i] != nil {
|
||||
if err := m.Tolerations[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("tolerations" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("tolerations" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) validateVolumes(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Volumes) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Volumes); i++ {
|
||||
if swag.IsZero(m.Volumes[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Volumes[i] != nil {
|
||||
if err := m.Volumes[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("volumes" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("volumes" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this describe pod wrapper based on the context it is used
|
||||
func (m *DescribePodWrapper) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateAnnotations(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateConditions(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateContainers(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateLabels(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateNodeSelector(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateTolerations(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateVolumes(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) contextValidateAnnotations(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.Annotations); i++ {
|
||||
|
||||
if m.Annotations[i] != nil {
|
||||
if err := m.Annotations[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("annotations" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("annotations" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) contextValidateConditions(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.Conditions); i++ {
|
||||
|
||||
if m.Conditions[i] != nil {
|
||||
if err := m.Conditions[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("conditions" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("conditions" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) contextValidateContainers(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.Containers); i++ {
|
||||
|
||||
if m.Containers[i] != nil {
|
||||
if err := m.Containers[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("containers" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("containers" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) contextValidateLabels(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.Labels); i++ {
|
||||
|
||||
if m.Labels[i] != nil {
|
||||
if err := m.Labels[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("labels" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("labels" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) contextValidateNodeSelector(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.NodeSelector); i++ {
|
||||
|
||||
if m.NodeSelector[i] != nil {
|
||||
if err := m.NodeSelector[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("nodeSelector" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("nodeSelector" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) contextValidateTolerations(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.Tolerations); i++ {
|
||||
|
||||
if m.Tolerations[i] != nil {
|
||||
if err := m.Tolerations[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("tolerations" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("tolerations" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DescribePodWrapper) contextValidateVolumes(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.Volumes); i++ {
|
||||
|
||||
if m.Volumes[i] != nil {
|
||||
if err := m.Volumes[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("volumes" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("volumes" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *DescribePodWrapper) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *DescribePodWrapper) UnmarshalBinary(b []byte) error {
|
||||
var res DescribePodWrapper
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
70
models/environment_variable.go
Normal file
70
models/environment_variable.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// EnvironmentVariable environment variable
|
||||
//
|
||||
// swagger:model environmentVariable
|
||||
type EnvironmentVariable struct {
|
||||
|
||||
// key
|
||||
Key string `json:"key,omitempty"`
|
||||
|
||||
// value
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this environment variable
|
||||
func (m *EnvironmentVariable) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this environment variable based on context it is used
|
||||
func (m *EnvironmentVariable) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *EnvironmentVariable) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *EnvironmentVariable) UnmarshalBinary(b []byte) error {
|
||||
var res EnvironmentVariable
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
76
models/mount.go
Normal file
76
models/mount.go
Normal file
@@ -0,0 +1,76 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// Mount mount
|
||||
//
|
||||
// swagger:model mount
|
||||
type Mount struct {
|
||||
|
||||
// mount path
|
||||
MountPath string `json:"mountPath,omitempty"`
|
||||
|
||||
// name
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// read only
|
||||
ReadOnly bool `json:"readOnly,omitempty"`
|
||||
|
||||
// sub path
|
||||
SubPath string `json:"subPath,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this mount
|
||||
func (m *Mount) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this mount based on context it is used
|
||||
func (m *Mount) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *Mount) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *Mount) UnmarshalBinary(b []byte) error {
|
||||
var res Mount
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
133
models/projected_volume.go
Normal file
133
models/projected_volume.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// ProjectedVolume projected volume
|
||||
//
|
||||
// swagger:model projectedVolume
|
||||
type ProjectedVolume struct {
|
||||
|
||||
// sources
|
||||
Sources []*ProjectedVolumeSource `json:"sources"`
|
||||
}
|
||||
|
||||
// Validate validates this projected volume
|
||||
func (m *ProjectedVolume) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateSources(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProjectedVolume) validateSources(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Sources) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Sources); i++ {
|
||||
if swag.IsZero(m.Sources[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Sources[i] != nil {
|
||||
if err := m.Sources[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("sources" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("sources" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this projected volume based on the context it is used
|
||||
func (m *ProjectedVolume) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateSources(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProjectedVolume) contextValidateSources(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.Sources); i++ {
|
||||
|
||||
if m.Sources[i] != nil {
|
||||
if err := m.Sources[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("sources" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("sources" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *ProjectedVolume) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *ProjectedVolume) UnmarshalBinary(b []byte) error {
|
||||
var res ProjectedVolume
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
216
models/projected_volume_source.go
Normal file
216
models/projected_volume_source.go
Normal file
@@ -0,0 +1,216 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// ProjectedVolumeSource projected volume source
|
||||
//
|
||||
// swagger:model projectedVolumeSource
|
||||
type ProjectedVolumeSource struct {
|
||||
|
||||
// config map
|
||||
ConfigMap *ConfigMap `json:"configMap,omitempty"`
|
||||
|
||||
// downward Api
|
||||
DownwardAPI bool `json:"downwardApi,omitempty"`
|
||||
|
||||
// secret
|
||||
Secret *Secret `json:"secret,omitempty"`
|
||||
|
||||
// service account token
|
||||
ServiceAccountToken *ServiceAccountToken `json:"serviceAccountToken,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this projected volume source
|
||||
func (m *ProjectedVolumeSource) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateConfigMap(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateSecret(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateServiceAccountToken(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProjectedVolumeSource) validateConfigMap(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.ConfigMap) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.ConfigMap != nil {
|
||||
if err := m.ConfigMap.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("configMap")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("configMap")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProjectedVolumeSource) validateSecret(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Secret) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Secret != nil {
|
||||
if err := m.Secret.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("secret")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("secret")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProjectedVolumeSource) validateServiceAccountToken(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.ServiceAccountToken) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.ServiceAccountToken != nil {
|
||||
if err := m.ServiceAccountToken.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("serviceAccountToken")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("serviceAccountToken")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this projected volume source based on the context it is used
|
||||
func (m *ProjectedVolumeSource) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateConfigMap(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateSecret(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidateServiceAccountToken(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProjectedVolumeSource) contextValidateConfigMap(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.ConfigMap != nil {
|
||||
if err := m.ConfigMap.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("configMap")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("configMap")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProjectedVolumeSource) contextValidateSecret(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.Secret != nil {
|
||||
if err := m.Secret.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("secret")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("secret")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProjectedVolumeSource) contextValidateServiceAccountToken(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.ServiceAccountToken != nil {
|
||||
if err := m.ServiceAccountToken.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("serviceAccountToken")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("serviceAccountToken")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *ProjectedVolumeSource) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *ProjectedVolumeSource) UnmarshalBinary(b []byte) error {
|
||||
var res ProjectedVolumeSource
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
70
models/pvc.go
Normal file
70
models/pvc.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// Pvc pvc
|
||||
//
|
||||
// swagger:model pvc
|
||||
type Pvc struct {
|
||||
|
||||
// claim name
|
||||
ClaimName string `json:"claimName,omitempty"`
|
||||
|
||||
// read only
|
||||
ReadOnly bool `json:"readOnly,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this pvc
|
||||
func (m *Pvc) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this pvc based on context it is used
|
||||
func (m *Pvc) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *Pvc) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *Pvc) UnmarshalBinary(b []byte) error {
|
||||
var res Pvc
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
70
models/secret.go
Normal file
70
models/secret.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// Secret secret
|
||||
//
|
||||
// swagger:model secret
|
||||
type Secret struct {
|
||||
|
||||
// name
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// optional
|
||||
Optional bool `json:"optional,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this secret
|
||||
func (m *Secret) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this secret based on context it is used
|
||||
func (m *Secret) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *Secret) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *Secret) UnmarshalBinary(b []byte) error {
|
||||
var res Secret
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
67
models/service_account_token.go
Normal file
67
models/service_account_token.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// ServiceAccountToken service account token
|
||||
//
|
||||
// swagger:model serviceAccountToken
|
||||
type ServiceAccountToken struct {
|
||||
|
||||
// expiration seconds
|
||||
ExpirationSeconds int64 `json:"expirationSeconds,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this service account token
|
||||
func (m *ServiceAccountToken) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this service account token based on context it is used
|
||||
func (m *ServiceAccountToken) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *ServiceAccountToken) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *ServiceAccountToken) UnmarshalBinary(b []byte) error {
|
||||
var res ServiceAccountToken
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
85
models/state.go
Normal file
85
models/state.go
Normal file
@@ -0,0 +1,85 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// State state
|
||||
//
|
||||
// swagger:model state
|
||||
type State struct {
|
||||
|
||||
// exit code
|
||||
ExitCode int64 `json:"exitCode,omitempty"`
|
||||
|
||||
// finished
|
||||
Finished string `json:"finished,omitempty"`
|
||||
|
||||
// message
|
||||
Message string `json:"message,omitempty"`
|
||||
|
||||
// reason
|
||||
Reason string `json:"reason,omitempty"`
|
||||
|
||||
// signal
|
||||
Signal int64 `json:"signal,omitempty"`
|
||||
|
||||
// started
|
||||
Started string `json:"started,omitempty"`
|
||||
|
||||
// state
|
||||
State string `json:"state,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this state
|
||||
func (m *State) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this state based on context it is used
|
||||
func (m *State) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *State) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *State) UnmarshalBinary(b []byte) error {
|
||||
var res State
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
79
models/toleration.go
Normal file
79
models/toleration.go
Normal file
@@ -0,0 +1,79 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// Toleration toleration
|
||||
//
|
||||
// swagger:model toleration
|
||||
type Toleration struct {
|
||||
|
||||
// effect
|
||||
Effect string `json:"effect,omitempty"`
|
||||
|
||||
// key
|
||||
Key string `json:"key,omitempty"`
|
||||
|
||||
// operator
|
||||
Operator string `json:"operator,omitempty"`
|
||||
|
||||
// toleration seconds
|
||||
TolerationSeconds int64 `json:"tolerationSeconds,omitempty"`
|
||||
|
||||
// value
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this toleration
|
||||
func (m *Toleration) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this toleration based on context it is used
|
||||
func (m *Toleration) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *Toleration) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *Toleration) UnmarshalBinary(b []byte) error {
|
||||
var res Toleration
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
170
models/volume.go
Normal file
170
models/volume.go
Normal file
@@ -0,0 +1,170 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// Volume volume
|
||||
//
|
||||
// swagger:model volume
|
||||
type Volume struct {
|
||||
|
||||
// name
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// projected
|
||||
Projected *ProjectedVolume `json:"projected,omitempty"`
|
||||
|
||||
// pvc
|
||||
Pvc *Pvc `json:"pvc,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this volume
|
||||
func (m *Volume) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateProjected(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validatePvc(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Volume) validateProjected(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Projected) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Projected != nil {
|
||||
if err := m.Projected.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("projected")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("projected")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Volume) validatePvc(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Pvc) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Pvc != nil {
|
||||
if err := m.Pvc.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("pvc")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("pvc")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this volume based on the context it is used
|
||||
func (m *Volume) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateProjected(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.contextValidatePvc(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Volume) contextValidateProjected(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.Projected != nil {
|
||||
if err := m.Projected.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("projected")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("projected")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Volume) contextValidatePvc(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.Pvc != nil {
|
||||
if err := m.Pvc.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("pvc")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("pvc")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *Volume) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *Volume) UnmarshalBinary(b []byte) error {
|
||||
var res Volume
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -564,3 +564,48 @@ func TestGetPodEvents(t *testing.T) {
|
||||
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")
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -18,15 +18,14 @@ package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
errors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
operatorClientset "github.com/minio/operator/pkg/client/clientset/versioned"
|
||||
)
|
||||
|
||||
var errInvalidCredentials = errors.New("invalid Login")
|
||||
|
||||
// operatorCredentialsProvider is an struct to hold the JWT (service account token)
|
||||
type operatorCredentialsProvider struct {
|
||||
serviceAccountJWT string
|
||||
@@ -86,7 +85,7 @@ func GetConsoleCredentialsForOperator(jwt string) (*credentials.Credentials, err
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err = checkServiceAccountTokenValid(ctx, opClient); err != nil {
|
||||
return nil, errInvalidCredentials
|
||||
return nil, errors.ErrInvalidLogin
|
||||
}
|
||||
return credentials.New(operatorCredentialsProvider{serviceAccountJWT: jwt}), nil
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/klauspost/compress/gzhttp"
|
||||
|
||||
"github.com/minio/console/restapi"
|
||||
"github.com/unrolled/secure"
|
||||
|
||||
@@ -58,7 +57,7 @@ func configureAPI(api *operations.OperatorAPI) http.Handler {
|
||||
api.KeyAuth = func(token string, scopes []string) (*models.Principal, error) {
|
||||
// we are validating the session token by decrypting the claims inside, if the operation succeed that means the jwt
|
||||
// was generated and signed by us in the first place
|
||||
claims, err := auth.SessionTokenAuthenticate(token)
|
||||
claims, err := auth.ParseClaimsFromToken(token)
|
||||
if err != nil {
|
||||
api.Logger("Unable to validate the session token %s: %v", token, err)
|
||||
return nil, errors.New(401, "incorrect api key auth")
|
||||
@@ -101,8 +100,8 @@ func configureAPI(api *operations.OperatorAPI) http.Handler {
|
||||
|
||||
// The TLS configuration before HTTPS server starts.
|
||||
func configureTLS(tlsConfig *tls.Config) {
|
||||
tlsConfig.RootCAs = GlobalRootCAs
|
||||
tlsConfig.GetCertificate = GlobalTLSCertsManager.GetCertificate
|
||||
tlsConfig.RootCAs = restapi.GlobalRootCAs
|
||||
tlsConfig.GetCertificate = restapi.GlobalTLSCertsManager.GetCertificate
|
||||
}
|
||||
|
||||
// As soon as server is initialized but not run yet, this function will be called.
|
||||
@@ -118,24 +117,6 @@ func setupMiddlewares(handler http.Handler) http.Handler {
|
||||
return handler
|
||||
}
|
||||
|
||||
func AuthenticationMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
token, err := auth.GetTokenFromRequest(r)
|
||||
if err != nil && err != auth.ErrNoAuthToken {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
// All handlers handle appropriately to return errors
|
||||
// based on their swagger rules, we do not need to
|
||||
// additionally return error here, let the next ServeHTTPs
|
||||
// handle it appropriately.
|
||||
if token != "" {
|
||||
r.Header.Add("Authorization", "Bearer "+token)
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// proxyMiddleware adds the proxy capability
|
||||
func proxyMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -150,19 +131,23 @@ func proxyMiddleware(next http.Handler) http.Handler {
|
||||
// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
|
||||
// So this is a good place to plug in a panic handling middleware, logging and metrics.
|
||||
func setupGlobalMiddleware(handler http.Handler) http.Handler {
|
||||
// handle cookie or authorization header for session
|
||||
next := AuthenticationMiddleware(handler)
|
||||
// proxy requests
|
||||
next = proxyMiddleware(next)
|
||||
next := proxyMiddleware(handler)
|
||||
// if audit-log is enabled console will log all incoming request
|
||||
next = restapi.AuditLogMiddleware(next)
|
||||
// serve static files
|
||||
next = restapi.FileServerMiddleware(next)
|
||||
// add information to request context
|
||||
next = restapi.ContextMiddleware(next)
|
||||
// handle cookie or authorization header for session
|
||||
next = restapi.AuthenticationMiddleware(next)
|
||||
// Secure middleware, this middleware wrap all the previous handlers and add
|
||||
// HTTP security headers
|
||||
secureOptions := secure.Options{
|
||||
AllowedHosts: restapi.GetSecureAllowedHosts(),
|
||||
AllowedHostsAreRegex: restapi.GetSecureAllowedHostsAreRegex(),
|
||||
HostsProxyHeaders: restapi.GetSecureHostsProxyHeaders(),
|
||||
SSLRedirect: restapi.GetTLSRedirect() == "on" && len(GlobalPublicCerts) > 0,
|
||||
SSLRedirect: restapi.GetTLSRedirect() == "on" && len(restapi.GlobalPublicCerts) > 0,
|
||||
SSLHost: restapi.GetSecureTLSHost(),
|
||||
STSSeconds: restapi.GetSecureSTSSeconds(),
|
||||
STSIncludeSubdomains: restapi.GetSecureSTSIncludeSubdomains(),
|
||||
|
||||
@@ -208,7 +208,7 @@ func init() {
|
||||
"get": {
|
||||
"security": [],
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Returns login strategy, form or sso.",
|
||||
"operationId": "LoginDetail",
|
||||
@@ -232,7 +232,7 @@ func init() {
|
||||
"post": {
|
||||
"security": [],
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Identity Provider oauth2 callback endpoint.",
|
||||
"operationId": "LoginOauth2Auth",
|
||||
@@ -263,7 +263,7 @@ func init() {
|
||||
"post": {
|
||||
"security": [],
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Login to Operator Console.",
|
||||
"operationId": "LoginOperator",
|
||||
@@ -293,7 +293,7 @@ func init() {
|
||||
"/logout": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Logout from Operator.",
|
||||
"operationId": "Logout",
|
||||
@@ -1200,6 +1200,49 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/namespaces/{namespace}/tenants/{tenant}/pods/{podName}/describe": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"OperatorAPI"
|
||||
],
|
||||
"summary": "Describe Pod",
|
||||
"operationId": "DescribePod",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "namespace",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "tenant",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "podName",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/describePodWrapper"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/namespaces/{namespace}/tenants/{tenant}/pods/{podName}/events": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -1665,7 +1708,7 @@ func init() {
|
||||
"/session": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Endpoint to check if your session is still valid",
|
||||
"operationId": "SessionCheck",
|
||||
@@ -2017,6 +2060,28 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"configMap": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"optional": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"configureTenantRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -2025,6 +2090,65 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"container": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"args": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"containerID": {
|
||||
"type": "string"
|
||||
},
|
||||
"environmentVariables": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/environmentVariable"
|
||||
}
|
||||
},
|
||||
"hostPorts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageID": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastState": {
|
||||
"$ref": "#/definitions/state"
|
||||
},
|
||||
"mounts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/mount"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"ports": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"ready": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"restartCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/state"
|
||||
}
|
||||
}
|
||||
},
|
||||
"createTenantRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -2157,6 +2281,95 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"describePodWrapper": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/annotation"
|
||||
}
|
||||
},
|
||||
"conditions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/condition"
|
||||
}
|
||||
},
|
||||
"containers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/container"
|
||||
}
|
||||
},
|
||||
"controllerRef": {
|
||||
"type": "string"
|
||||
},
|
||||
"deletionGracePeriodSeconds": {
|
||||
"type": "integer"
|
||||
},
|
||||
"deletionTimestamp": {
|
||||
"type": "string"
|
||||
},
|
||||
"labels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/label"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"type": "string"
|
||||
},
|
||||
"nodeName": {
|
||||
"type": "string"
|
||||
},
|
||||
"nodeSelector": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/nodeSelector"
|
||||
}
|
||||
},
|
||||
"phase": {
|
||||
"type": "string"
|
||||
},
|
||||
"podIP": {
|
||||
"type": "string"
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer"
|
||||
},
|
||||
"priorityClassName": {
|
||||
"type": "string"
|
||||
},
|
||||
"qosClass": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
},
|
||||
"startTime": {
|
||||
"type": "string"
|
||||
},
|
||||
"tolerations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/toleration"
|
||||
}
|
||||
},
|
||||
"volumes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/volume"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"directCSIDriveInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -2324,6 +2537,17 @@ func init() {
|
||||
}
|
||||
]
|
||||
},
|
||||
"environmentVariable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -2886,6 +3110,23 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"mount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mountPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"subPath": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"namespace": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -3357,6 +3598,34 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"projectedVolume": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/projectedVolumeSource"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"projectedVolumeSource": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"configMap": {
|
||||
"$ref": "#/definitions/configMap"
|
||||
},
|
||||
"downwardApi": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"secret": {
|
||||
"$ref": "#/definitions/secret"
|
||||
},
|
||||
"serviceAccountToken": {
|
||||
"$ref": "#/definitions/serviceAccountToken"
|
||||
}
|
||||
}
|
||||
},
|
||||
"prometheusConfiguration": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3383,6 +3652,17 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"pvc": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"claimName": {
|
||||
"type": "string"
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pvcsListResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3442,6 +3722,17 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"secret": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"optional": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityContext": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -3465,6 +3756,40 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceAccountToken": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expirationSeconds": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"exitCode": {
|
||||
"type": "integer"
|
||||
},
|
||||
"finished": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
},
|
||||
"signal": {
|
||||
"type": "integer"
|
||||
},
|
||||
"started": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subscriptionValidateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3901,6 +4226,26 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"toleration": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"effect": {
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"type": "string"
|
||||
},
|
||||
"tolerationSeconds": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"updateDomainsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -4088,6 +4433,20 @@ func init() {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"volume": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"projected": {
|
||||
"$ref": "#/definitions/projectedVolume"
|
||||
},
|
||||
"pvc": {
|
||||
"$ref": "#/definitions/pvc"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
@@ -4278,7 +4637,7 @@ func init() {
|
||||
"get": {
|
||||
"security": [],
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Returns login strategy, form or sso.",
|
||||
"operationId": "LoginDetail",
|
||||
@@ -4302,7 +4661,7 @@ func init() {
|
||||
"post": {
|
||||
"security": [],
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Identity Provider oauth2 callback endpoint.",
|
||||
"operationId": "LoginOauth2Auth",
|
||||
@@ -4333,7 +4692,7 @@ func init() {
|
||||
"post": {
|
||||
"security": [],
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Login to Operator Console.",
|
||||
"operationId": "LoginOperator",
|
||||
@@ -4363,7 +4722,7 @@ func init() {
|
||||
"/logout": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Logout from Operator.",
|
||||
"operationId": "Logout",
|
||||
@@ -5270,6 +5629,49 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/namespaces/{namespace}/tenants/{tenant}/pods/{podName}/describe": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"OperatorAPI"
|
||||
],
|
||||
"summary": "Describe Pod",
|
||||
"operationId": "DescribePod",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "namespace",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "tenant",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "podName",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/describePodWrapper"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/namespaces/{namespace}/tenants/{tenant}/pods/{podName}/events": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -5735,7 +6137,7 @@ func init() {
|
||||
"/session": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
"Auth"
|
||||
],
|
||||
"summary": "Endpoint to check if your session is still valid",
|
||||
"operationId": "SessionCheck",
|
||||
@@ -6930,6 +7332,28 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"configMap": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"optional": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"configureTenantRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -6938,6 +7362,65 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"container": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"args": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"containerID": {
|
||||
"type": "string"
|
||||
},
|
||||
"environmentVariables": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/environmentVariable"
|
||||
}
|
||||
},
|
||||
"hostPorts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageID": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastState": {
|
||||
"$ref": "#/definitions/state"
|
||||
},
|
||||
"mounts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/mount"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"ports": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"ready": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"restartCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/state"
|
||||
}
|
||||
}
|
||||
},
|
||||
"createTenantRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -7070,6 +7553,95 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"describePodWrapper": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/annotation"
|
||||
}
|
||||
},
|
||||
"conditions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/condition"
|
||||
}
|
||||
},
|
||||
"containers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/container"
|
||||
}
|
||||
},
|
||||
"controllerRef": {
|
||||
"type": "string"
|
||||
},
|
||||
"deletionGracePeriodSeconds": {
|
||||
"type": "integer"
|
||||
},
|
||||
"deletionTimestamp": {
|
||||
"type": "string"
|
||||
},
|
||||
"labels": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/label"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"type": "string"
|
||||
},
|
||||
"nodeName": {
|
||||
"type": "string"
|
||||
},
|
||||
"nodeSelector": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/nodeSelector"
|
||||
}
|
||||
},
|
||||
"phase": {
|
||||
"type": "string"
|
||||
},
|
||||
"podIP": {
|
||||
"type": "string"
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer"
|
||||
},
|
||||
"priorityClassName": {
|
||||
"type": "string"
|
||||
},
|
||||
"qosClass": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
},
|
||||
"startTime": {
|
||||
"type": "string"
|
||||
},
|
||||
"tolerations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/toleration"
|
||||
}
|
||||
},
|
||||
"volumes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/volume"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"directCSIDriveInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -7237,6 +7809,17 @@ func init() {
|
||||
}
|
||||
]
|
||||
},
|
||||
"environmentVariable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -7787,6 +8370,23 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"mount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mountPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"subPath": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"namespace": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -8123,6 +8723,34 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"projectedVolume": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/projectedVolumeSource"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"projectedVolumeSource": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"configMap": {
|
||||
"$ref": "#/definitions/configMap"
|
||||
},
|
||||
"downwardApi": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"secret": {
|
||||
"$ref": "#/definitions/secret"
|
||||
},
|
||||
"serviceAccountToken": {
|
||||
"$ref": "#/definitions/serviceAccountToken"
|
||||
}
|
||||
}
|
||||
},
|
||||
"prometheusConfiguration": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -8149,6 +8777,17 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"pvc": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"claimName": {
|
||||
"type": "string"
|
||||
},
|
||||
"readOnly": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pvcsListResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -8208,6 +8847,17 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"secret": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"optional": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityContext": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -8231,6 +8881,40 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceAccountToken": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expirationSeconds": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"exitCode": {
|
||||
"type": "integer"
|
||||
},
|
||||
"finished": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
},
|
||||
"signal": {
|
||||
"type": "integer"
|
||||
},
|
||||
"started": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subscriptionValidateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -8667,6 +9351,26 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"toleration": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"effect": {
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"type": "string"
|
||||
},
|
||||
"tolerationSeconds": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"updateDomainsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -8854,6 +9558,20 @@ func init() {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"volume": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"projected": {
|
||||
"$ref": "#/definitions/projectedVolume"
|
||||
},
|
||||
"pvc": {
|
||||
"$ref": "#/definitions/pvc"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
// 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 operatorapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go"
|
||||
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// Generic error messages
|
||||
errorGeneric = errors.New("an error occurred, please try again")
|
||||
errInvalidCredentials = errors.New("invalid Login")
|
||||
errorGenericInvalidSession = errors.New("invalid session")
|
||||
errorGenericUnauthorized = errors.New("unauthorized")
|
||||
errorGenericForbidden = errors.New("forbidden")
|
||||
// ErrorGenericNotFound Generic error for not found
|
||||
ErrorGenericNotFound = errors.New("not found")
|
||||
// Explicit error messages
|
||||
errorInvalidErasureCodingValue = errors.New("invalid Erasure Coding Value")
|
||||
errorUnableToGetTenantUsage = errors.New("unable to get tenant usage")
|
||||
errorUnableToGetTenantLogs = errors.New("unable to get tenant logs")
|
||||
errorUnableToUpdateTenantCertificates = errors.New("unable to update tenant certificates")
|
||||
errorUpdatingEncryptionConfig = errors.New("unable to update encryption configuration")
|
||||
errorDeletingEncryptionConfig = errors.New("error disabling tenant encryption")
|
||||
errorEncryptionConfigNotFound = errors.New("encryption configuration not found")
|
||||
errBucketBodyNotInRequest = errors.New("error bucket body not in request")
|
||||
errBucketNameNotInRequest = errors.New("error bucket name not in request")
|
||||
errGroupBodyNotInRequest = errors.New("error group body not in request")
|
||||
errGroupNameNotInRequest = errors.New("error group name not in request")
|
||||
errPolicyNameNotInRequest = errors.New("error policy name not in request")
|
||||
errPolicyBodyNotInRequest = errors.New("error policy body not in request")
|
||||
errSSENotConfigured = errors.New("error server side encryption configuration not found")
|
||||
errBucketLifeCycleNotConfigured = errors.New("error bucket life cycle configuration not found")
|
||||
errChangePassword = errors.New("error please check your current password")
|
||||
errInvalidLicense = errors.New("invalid license key")
|
||||
errLicenseNotFound = errors.New("license not found")
|
||||
errAvoidSelfAccountDelete = errors.New("logged in user cannot be deleted by itself")
|
||||
errAccessDenied = errors.New("access denied")
|
||||
errTooManyNodes = errors.New("cannot request more nodes than what is available in the cluster")
|
||||
errTooFewNodes = errors.New("there are not enough nodes in the cluster to support this tenant")
|
||||
errTooFewSchedulableNodes = errors.New("there is not enough schedulable nodes to satisfy this requirement")
|
||||
errFewerThanFourNodes = errors.New("at least 4 nodes are required for a tenant")
|
||||
)
|
||||
|
||||
// prepareError receives an error object and parse it against k8sErrors, returns the right error code paired with a generic error message
|
||||
func prepareError(err ...error) *models.Error {
|
||||
errorCode := int32(500)
|
||||
errorMessage := errorGeneric.Error()
|
||||
if len(err) > 0 {
|
||||
frame := getFrame(2)
|
||||
fileParts := strings.Split(frame.File, "/")
|
||||
LogError("original error -> (%s:%d: %v)", fileParts[len(fileParts)-1], frame.Line, err[0])
|
||||
if k8sErrors.IsUnauthorized(err[0]) {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericUnauthorized.Error()
|
||||
}
|
||||
if k8sErrors.IsForbidden(err[0]) {
|
||||
errorCode = 403
|
||||
errorMessage = errorGenericForbidden.Error()
|
||||
}
|
||||
if k8sErrors.IsNotFound(err[0]) {
|
||||
errorCode = 404
|
||||
errorMessage = ErrorGenericNotFound.Error()
|
||||
}
|
||||
if err[0] == ErrorGenericNotFound {
|
||||
errorCode = 404
|
||||
errorMessage = ErrorGenericNotFound.Error()
|
||||
}
|
||||
if errors.Is(err[0], errInvalidCredentials) {
|
||||
errorCode = 401
|
||||
errorMessage = errInvalidCredentials.Error()
|
||||
}
|
||||
// console invalid erasure coding value
|
||||
if errors.Is(err[0], errorInvalidErasureCodingValue) {
|
||||
errorCode = 400
|
||||
errorMessage = errorInvalidErasureCodingValue.Error()
|
||||
}
|
||||
if errors.Is(err[0], errBucketBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errBucketBodyNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errBucketNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errBucketNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errGroupBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errGroupBodyNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errGroupNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errGroupNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errPolicyNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errPolicyNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errPolicyBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errPolicyBodyNotInRequest.Error()
|
||||
}
|
||||
// console invalid session error
|
||||
if errors.Is(err[0], errorGenericInvalidSession) {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericInvalidSession.Error()
|
||||
}
|
||||
// Bucket life cycle not configured
|
||||
if errors.Is(err[0], errBucketLifeCycleNotConfigured) {
|
||||
errorCode = 404
|
||||
errorMessage = errBucketLifeCycleNotConfigured.Error()
|
||||
}
|
||||
// Encryption not configured
|
||||
if errors.Is(err[0], errSSENotConfigured) {
|
||||
errorCode = 404
|
||||
errorMessage = errSSENotConfigured.Error()
|
||||
}
|
||||
// account change password
|
||||
if madmin.ToErrorResponse(err[0]).Code == "SignatureDoesNotMatch" {
|
||||
errorCode = 403
|
||||
errorMessage = errChangePassword.Error()
|
||||
}
|
||||
if errors.Is(err[0], errLicenseNotFound) {
|
||||
errorCode = 404
|
||||
errorMessage = errLicenseNotFound.Error()
|
||||
}
|
||||
if errors.Is(err[0], errInvalidLicense) {
|
||||
errorCode = 404
|
||||
errorMessage = errInvalidLicense.Error()
|
||||
}
|
||||
if errors.Is(err[0], errAvoidSelfAccountDelete) {
|
||||
errorCode = 403
|
||||
errorMessage = errAvoidSelfAccountDelete.Error()
|
||||
}
|
||||
if madmin.ToErrorResponse(err[0]).Code == "AccessDenied" {
|
||||
errorCode = 403
|
||||
errorMessage = errAccessDenied.Error()
|
||||
}
|
||||
if madmin.ToErrorResponse(err[0]).Code == "InvalidAccessKeyId" {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericInvalidSession.Error()
|
||||
}
|
||||
// console invalid session error
|
||||
if madmin.ToErrorResponse(err[0]).Code == "XMinioAdminNoSuchUser" {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericInvalidSession.Error()
|
||||
}
|
||||
// if we received a second error take that as friendly message but dont override the code
|
||||
if len(err) > 1 && err[1] != nil {
|
||||
LogError("friendly error: %v", err[1].Error())
|
||||
errorMessage = err[1].Error()
|
||||
}
|
||||
// if we receive third error we just print that as debugging
|
||||
if len(err) > 2 && err[2] != nil {
|
||||
LogError("debugging error: %v", err[2].Error())
|
||||
}
|
||||
|
||||
errRemoteTierExists := errors.New("Specified remote tier already exists") //nolint
|
||||
if errors.Is(err[0], errRemoteTierExists) {
|
||||
errorMessage = err[0].Error()
|
||||
}
|
||||
if errors.Is(err[0], errTooFewNodes) {
|
||||
errorCode = 507
|
||||
errorMessage = errTooFewNodes.Error()
|
||||
}
|
||||
if errors.Is(err[0], errTooFewSchedulableNodes) {
|
||||
errorCode = 507
|
||||
errorMessage = errTooFewSchedulableNodes.Error()
|
||||
}
|
||||
if errors.Is(err[0], errFewerThanFourNodes) {
|
||||
errorCode = 507
|
||||
errorMessage = errFewerThanFourNodes.Error()
|
||||
}
|
||||
}
|
||||
return &models.Error{Code: errorCode, Message: swag.String(errorMessage), DetailedMessage: swag.String(err[0].Error())}
|
||||
}
|
||||
|
||||
func getFrame(skipFrames int) runtime.Frame {
|
||||
// We need the frame at index skipFrames+2, since we never want runtime.Callers and getFrame
|
||||
targetFrameIndex := skipFrames + 2
|
||||
|
||||
// Set size to targetFrameIndex+2 to ensure we have room for one more caller than we need
|
||||
programCounters := make([]uintptr, targetFrameIndex+2)
|
||||
n := runtime.Callers(0, programCounters)
|
||||
|
||||
frame := runtime.Frame{Function: "unknown"}
|
||||
if n > 0 {
|
||||
frames := runtime.CallersFrames(programCounters[:n])
|
||||
for more, frameIndex := true, 0; more && frameIndex <= targetFrameIndex; frameIndex++ {
|
||||
var frameCandidate runtime.Frame
|
||||
frameCandidate, more = frames.Next()
|
||||
if frameIndex == targetFrameIndex {
|
||||
frame = frameCandidate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return frame
|
||||
}
|
||||
@@ -33,44 +33,45 @@ import (
|
||||
"github.com/minio/console/models"
|
||||
opauth "github.com/minio/console/operatorapi/auth"
|
||||
"github.com/minio/console/operatorapi/operations"
|
||||
"github.com/minio/console/operatorapi/operations/user_api"
|
||||
authApi "github.com/minio/console/operatorapi/operations/auth"
|
||||
|
||||
"github.com/minio/console/pkg/auth"
|
||||
"github.com/minio/console/pkg/auth/idp/oauth2"
|
||||
)
|
||||
|
||||
func registerLoginHandlers(api *operations.OperatorAPI) {
|
||||
// GET login strategy
|
||||
api.UserAPILoginDetailHandler = user_api.LoginDetailHandlerFunc(func(params user_api.LoginDetailParams) middleware.Responder {
|
||||
loginDetails, err := getLoginDetailsResponse(params.HTTPRequest)
|
||||
api.AuthLoginDetailHandler = authApi.LoginDetailHandlerFunc(func(params authApi.LoginDetailParams) middleware.Responder {
|
||||
loginDetails, err := getLoginDetailsResponse(params)
|
||||
if err != nil {
|
||||
return user_api.NewLoginDetailDefault(int(err.Code)).WithPayload(err)
|
||||
return authApi.NewLoginDetailDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewLoginDetailOK().WithPayload(loginDetails)
|
||||
return authApi.NewLoginDetailOK().WithPayload(loginDetails)
|
||||
})
|
||||
// POST login using k8s service account token
|
||||
api.UserAPILoginOperatorHandler = user_api.LoginOperatorHandlerFunc(func(params user_api.LoginOperatorParams) middleware.Responder {
|
||||
loginResponse, err := getLoginOperatorResponse(params.Body)
|
||||
api.AuthLoginOperatorHandler = authApi.LoginOperatorHandlerFunc(func(params authApi.LoginOperatorParams) middleware.Responder {
|
||||
loginResponse, err := getLoginOperatorResponse(params)
|
||||
if err != nil {
|
||||
return user_api.NewLoginOperatorDefault(int(err.Code)).WithPayload(err)
|
||||
return authApi.NewLoginOperatorDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
// Custom response writer to set the session cookies
|
||||
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
|
||||
cookie := restapi.NewSessionCookieForConsole(loginResponse.SessionID)
|
||||
http.SetCookie(w, &cookie)
|
||||
user_api.NewLoginOperatorNoContent().WriteResponse(w, p)
|
||||
authApi.NewLoginOperatorNoContent().WriteResponse(w, p)
|
||||
})
|
||||
})
|
||||
// POST login using external IDP
|
||||
api.UserAPILoginOauth2AuthHandler = user_api.LoginOauth2AuthHandlerFunc(func(params user_api.LoginOauth2AuthParams) middleware.Responder {
|
||||
loginResponse, err := getLoginOauth2AuthResponse(params.HTTPRequest, params.Body)
|
||||
api.AuthLoginOauth2AuthHandler = authApi.LoginOauth2AuthHandlerFunc(func(params authApi.LoginOauth2AuthParams) middleware.Responder {
|
||||
loginResponse, err := getLoginOauth2AuthResponse(params)
|
||||
if err != nil {
|
||||
return user_api.NewLoginOauth2AuthDefault(int(err.Code)).WithPayload(err)
|
||||
return authApi.NewLoginOauth2AuthDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
// Custom response writer to set the session cookies
|
||||
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
|
||||
cookie := restapi.NewSessionCookieForConsole(loginResponse.SessionID)
|
||||
http.SetCookie(w, &cookie)
|
||||
user_api.NewLoginOauth2AuthNoContent().WriteResponse(w, p)
|
||||
authApi.NewLoginOauth2AuthNoContent().WriteResponse(w, p)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -86,14 +87,19 @@ func login(credentials restapi.ConsoleCredentialsI) (*string, error) {
|
||||
// if we made it here, the consoleCredentials work, generate a jwt with claims
|
||||
token, err := auth.NewEncryptedTokenForClient(&tokens, credentials.GetAccountAccessKey(), nil)
|
||||
if err != nil {
|
||||
LogError("error authenticating user: %v", err)
|
||||
return nil, errInvalidCredentials
|
||||
restapi.LogError("error authenticating user: %v", err)
|
||||
return nil, restapi.ErrInvalidLogin
|
||||
}
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
// getLoginDetailsResponse returns information regarding the Console authentication mechanism.
|
||||
func getLoginDetailsResponse(r *http.Request) (*models.LoginDetails, *models.Error) {
|
||||
func getLoginDetailsResponse(params authApi.LoginDetailParams) (*models.LoginDetails, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
r := params.HTTPRequest
|
||||
|
||||
loginStrategy := models.LoginDetailsLoginStrategyServiceDashAccount
|
||||
redirectURL := ""
|
||||
|
||||
@@ -102,7 +108,7 @@ func getLoginDetailsResponse(r *http.Request) (*models.LoginDetails, *models.Err
|
||||
// initialize new oauth2 client
|
||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, restapi.GetConsoleHTTPClient())
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// Validate user against IDP
|
||||
identityProvider := &auth.IdentityProvider{Client: oauth2Client}
|
||||
@@ -125,31 +131,35 @@ func verifyUserAgainstIDP(ctx context.Context, provider auth.IdentityProviderI,
|
||||
return oauth2Token, nil
|
||||
}
|
||||
|
||||
func getLoginOauth2AuthResponse(r *http.Request, lr *models.LoginOauth2AuthRequest) (*models.LoginResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getLoginOauth2AuthResponse(params authApi.LoginOauth2AuthParams) (*models.LoginResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
r := params.HTTPRequest
|
||||
lr := params.Body
|
||||
|
||||
if oauth2.IsIDPEnabled() {
|
||||
// initialize new oauth2 client
|
||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, restapi.GetConsoleHTTPClient())
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// initialize new identity provider
|
||||
identityProvider := auth.IdentityProvider{Client: oauth2Client}
|
||||
// Validate user against IDP
|
||||
_, err = verifyUserAgainstIDP(ctx, identityProvider, *lr.Code, *lr.State)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// If we pass here that means the IDP correctly authenticate the user with the operator resource
|
||||
// we proceed to use the service account token configured in the operator-console pod
|
||||
creds, err := newConsoleCredentials(getK8sSAToken())
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
token, err := login(restapi.ConsoleCredentials{ConsoleCredentials: creds})
|
||||
if err != nil {
|
||||
return nil, prepareError(errInvalidCredentials, nil, err)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrInvalidLogin, nil, err)
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
@@ -157,7 +167,7 @@ func getLoginOauth2AuthResponse(r *http.Request, lr *models.LoginOauth2AuthReque
|
||||
}
|
||||
return loginResponse, nil
|
||||
}
|
||||
return nil, prepareError(errorGeneric)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrDefault)
|
||||
}
|
||||
|
||||
func newConsoleCredentials(secretKey string) (*credentials.Credentials, error) {
|
||||
@@ -169,17 +179,22 @@ func newConsoleCredentials(secretKey string) (*credentials.Credentials, error) {
|
||||
}
|
||||
|
||||
// getLoginOperatorResponse validate the provided service account token against k8s api
|
||||
func getLoginOperatorResponse(lmr *models.LoginOperatorRequest) (*models.LoginResponse, *models.Error) {
|
||||
func getLoginOperatorResponse(params authApi.LoginOperatorParams) (*models.LoginResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
lmr := params.Body
|
||||
|
||||
creds, err := newConsoleCredentials(*lmr.Jwt)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
consoleCreds := restapi.ConsoleCredentials{ConsoleCredentials: creds}
|
||||
// Set a random as access key as session identifier
|
||||
consoleCreds.AccountAccessKey = fmt.Sprintf("%d", rand.Intn(100000-10000)+10000)
|
||||
token, err := login(consoleCreds)
|
||||
if err != nil {
|
||||
return nil, prepareError(errInvalidCredentials, nil, err)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrInvalidLogin, nil, err)
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
|
||||
@@ -23,20 +23,20 @@ import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations"
|
||||
"github.com/minio/console/operatorapi/operations/user_api"
|
||||
authApi "github.com/minio/console/operatorapi/operations/auth"
|
||||
"github.com/minio/console/restapi"
|
||||
)
|
||||
|
||||
func registerLogoutHandlers(api *operations.OperatorAPI) {
|
||||
// logout from console
|
||||
api.UserAPILogoutHandler = user_api.LogoutHandlerFunc(func(params user_api.LogoutParams, session *models.Principal) middleware.Responder {
|
||||
api.AuthLogoutHandler = authApi.LogoutHandlerFunc(func(params authApi.LogoutParams, session *models.Principal) middleware.Responder {
|
||||
// Custom response writer to expire the session cookies
|
||||
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
|
||||
expiredCookie := restapi.ExpireSessionCookie()
|
||||
// this will tell the browser to clear the cookie and invalidate user session
|
||||
// additionally we are deleting the cookie from the client side
|
||||
http.SetCookie(w, &expiredCookie)
|
||||
user_api.NewLogoutOK().WriteResponse(w, p)
|
||||
authApi.NewLogoutOK().WriteResponse(w, p)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -20,12 +20,13 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
xerrors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations"
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
@@ -44,12 +45,12 @@ func registerNamespaceHandlers(api *operations.OperatorAPI) {
|
||||
}
|
||||
|
||||
func getNamespaceCreatedResponse(session *models.Principal, params operator_api.CreateNamespaceParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
namespace := *params.Body.Name
|
||||
@@ -57,7 +58,7 @@ func getNamespaceCreatedResponse(session *models.Principal, params operator_api.
|
||||
errCreation := getNamespaceCreated(ctx, clientset.CoreV1(), namespace)
|
||||
|
||||
if errCreation != nil {
|
||||
return prepareError(errCreation)
|
||||
return xerrors.ErrorWithContext(ctx, errCreation)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
xerrors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
@@ -52,8 +54,8 @@ func registerNodesHandlers(api *operations.OperatorAPI) {
|
||||
return operator_api.NewListNodeLabelsOK().WithPayload(*resp)
|
||||
})
|
||||
|
||||
api.OperatorAPIGetAllocatableResourcesHandler = operator_api.GetAllocatableResourcesHandlerFunc(func(params operator_api.GetAllocatableResourcesParams, principal *models.Principal) middleware.Responder {
|
||||
resp, err := getAllocatableResourcesResponse(params.NumNodes, principal)
|
||||
api.OperatorAPIGetAllocatableResourcesHandler = operator_api.GetAllocatableResourcesHandlerFunc(func(params operator_api.GetAllocatableResourcesParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := getAllocatableResourcesResponse(session, params)
|
||||
if err != nil {
|
||||
return operator_api.NewGetAllocatableResourcesDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -71,7 +73,7 @@ type NodeResourceInfo struct {
|
||||
func getMaxAllocatableMemory(ctx context.Context, clientset v1.CoreV1Interface, numNodes int32) (*models.MaxAllocatableMemResponse, error) {
|
||||
// can't request less than 4 nodes
|
||||
if numNodes < 4 {
|
||||
return nil, errFewerThanFourNodes
|
||||
return nil, xerrors.ErrFewerThanFourNodes
|
||||
}
|
||||
|
||||
// get all nodes from cluster
|
||||
@@ -97,15 +99,15 @@ func getMaxAllocatableMemory(ctx context.Context, clientset v1.CoreV1Interface,
|
||||
}
|
||||
// requesting more nodes than schedulable and less than total number of workers
|
||||
if int(numNodes) > schedulableNodes && int(numNodes) < nonMasterNodes {
|
||||
return nil, errTooManyNodes
|
||||
return nil, xerrors.ErrTooManyNodes
|
||||
}
|
||||
if nonMasterNodes < int(numNodes) {
|
||||
return nil, errTooFewNodes
|
||||
return nil, xerrors.ErrTooFewNodes
|
||||
}
|
||||
|
||||
// not enough schedulable nodes
|
||||
if schedulableNodes < int(numNodes) {
|
||||
return nil, errTooFewSchedulableNodes
|
||||
return nil, xerrors.ErrTooFewAvailableNodes
|
||||
}
|
||||
|
||||
availableMemSizes := []int64{}
|
||||
@@ -177,12 +179,12 @@ func min(x, y int64) int64 {
|
||||
func getMaxAllocatableMemoryResponse(ctx context.Context, session *models.Principal, numNodes int32) (*models.MaxAllocatableMemResponse, *models.Error) {
|
||||
client, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
clusterResources, err := getMaxAllocatableMemory(ctx, client.CoreV1(), numNodes)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return clusterResources, nil
|
||||
}
|
||||
@@ -217,12 +219,12 @@ func getNodeLabels(ctx context.Context, clientset v1.CoreV1Interface) (*models.N
|
||||
func getNodeLabelsResponse(ctx context.Context, session *models.Principal) (*models.NodeLabels, *models.Error) {
|
||||
client, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
clusterResources, err := getNodeLabels(ctx, client.CoreV1())
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return clusterResources, nil
|
||||
}
|
||||
@@ -357,17 +359,16 @@ OUTER:
|
||||
|
||||
// Get allocatable resources response
|
||||
|
||||
func getAllocatableResourcesResponse(numNodes int32, session *models.Principal) (*models.AllocatableResourcesResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getAllocatableResourcesResponse(session *models.Principal, params operator_api.GetAllocatableResourcesParams) (*models.AllocatableResourcesResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
client, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
clusterResources, err := getAllocatableResources(ctx, client.CoreV1(), numNodes)
|
||||
clusterResources, err := getAllocatableResources(ctx, client.CoreV1(), params.NumNodes)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return clusterResources, nil
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
@@ -46,7 +46,7 @@ func NewLoginDetail(ctx *middleware.Context, handler LoginDetailHandler) *LoginD
|
||||
return &LoginDetail{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* LoginDetail swagger:route GET /login UserAPI loginDetail
|
||||
/* LoginDetail swagger:route GET /login Auth loginDetail
|
||||
|
||||
Returns login strategy, form or sso.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
@@ -46,7 +46,7 @@ func NewLoginOauth2Auth(ctx *middleware.Context, handler LoginOauth2AuthHandler)
|
||||
return &LoginOauth2Auth{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* LoginOauth2Auth swagger:route POST /login/oauth2/auth UserAPI loginOauth2Auth
|
||||
/* LoginOauth2Auth swagger:route POST /login/oauth2/auth Auth loginOauth2Auth
|
||||
|
||||
Identity Provider oauth2 callback endpoint.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
@@ -46,7 +46,7 @@ func NewLoginOperator(ctx *middleware.Context, handler LoginOperatorHandler) *Lo
|
||||
return &LoginOperator{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* LoginOperator swagger:route POST /login/operator UserAPI loginOperator
|
||||
/* LoginOperator swagger:route POST /login/operator Auth loginOperator
|
||||
|
||||
Login to Operator Console.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
@@ -48,7 +48,7 @@ func NewLogout(ctx *middleware.Context, handler LogoutHandler) *Logout {
|
||||
return &Logout{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* Logout swagger:route POST /logout UserAPI logout
|
||||
/* Logout swagger:route POST /logout Auth logout
|
||||
|
||||
Logout from Operator.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
@@ -48,7 +48,7 @@ func NewSessionCheck(ctx *middleware.Context, handler SessionCheckHandler) *Sess
|
||||
return &SessionCheck{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* SessionCheck swagger:route GET /session UserAPI sessionCheck
|
||||
/* SessionCheck swagger:route GET /session Auth sessionCheck
|
||||
|
||||
Endpoint to check if your session is still valid
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
"github.com/go-openapi/swag"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations/auth"
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
"github.com/minio/console/operatorapi/operations/user_api"
|
||||
)
|
||||
@@ -81,6 +82,9 @@ func NewOperatorAPI(spec *loads.Document) *OperatorAPI {
|
||||
OperatorAPIDeleteTenantHandler: operator_api.DeleteTenantHandlerFunc(func(params operator_api.DeleteTenantParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation operator_api.DeleteTenant has not yet been implemented")
|
||||
}),
|
||||
OperatorAPIDescribePodHandler: operator_api.DescribePodHandlerFunc(func(params operator_api.DescribePodParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation operator_api.DescribePod has not yet been implemented")
|
||||
}),
|
||||
OperatorAPIDisableTenantLoggingHandler: operator_api.DisableTenantLoggingHandlerFunc(func(params operator_api.DisableTenantLoggingParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation operator_api.DisableTenantLogging has not yet been implemented")
|
||||
}),
|
||||
@@ -141,23 +145,23 @@ func NewOperatorAPI(spec *loads.Document) *OperatorAPI {
|
||||
OperatorAPIListTenantsHandler: operator_api.ListTenantsHandlerFunc(func(params operator_api.ListTenantsParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation operator_api.ListTenants has not yet been implemented")
|
||||
}),
|
||||
UserAPILoginDetailHandler: user_api.LoginDetailHandlerFunc(func(params user_api.LoginDetailParams) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.LoginDetail has not yet been implemented")
|
||||
AuthLoginDetailHandler: auth.LoginDetailHandlerFunc(func(params auth.LoginDetailParams) middleware.Responder {
|
||||
return middleware.NotImplemented("operation auth.LoginDetail has not yet been implemented")
|
||||
}),
|
||||
UserAPILoginOauth2AuthHandler: user_api.LoginOauth2AuthHandlerFunc(func(params user_api.LoginOauth2AuthParams) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.LoginOauth2Auth has not yet been implemented")
|
||||
AuthLoginOauth2AuthHandler: auth.LoginOauth2AuthHandlerFunc(func(params auth.LoginOauth2AuthParams) middleware.Responder {
|
||||
return middleware.NotImplemented("operation auth.LoginOauth2Auth has not yet been implemented")
|
||||
}),
|
||||
UserAPILoginOperatorHandler: user_api.LoginOperatorHandlerFunc(func(params user_api.LoginOperatorParams) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.LoginOperator has not yet been implemented")
|
||||
AuthLoginOperatorHandler: auth.LoginOperatorHandlerFunc(func(params auth.LoginOperatorParams) middleware.Responder {
|
||||
return middleware.NotImplemented("operation auth.LoginOperator has not yet been implemented")
|
||||
}),
|
||||
UserAPILogoutHandler: user_api.LogoutHandlerFunc(func(params user_api.LogoutParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.Logout has not yet been implemented")
|
||||
AuthLogoutHandler: auth.LogoutHandlerFunc(func(params auth.LogoutParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation auth.Logout has not yet been implemented")
|
||||
}),
|
||||
OperatorAPIPutTenantYAMLHandler: operator_api.PutTenantYAMLHandlerFunc(func(params operator_api.PutTenantYAMLParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation operator_api.PutTenantYAML has not yet been implemented")
|
||||
}),
|
||||
UserAPISessionCheckHandler: user_api.SessionCheckHandlerFunc(func(params user_api.SessionCheckParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.SessionCheck has not yet been implemented")
|
||||
AuthSessionCheckHandler: auth.SessionCheckHandlerFunc(func(params auth.SessionCheckParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation auth.SessionCheck has not yet been implemented")
|
||||
}),
|
||||
OperatorAPISetTenantLogsHandler: operator_api.SetTenantLogsHandlerFunc(func(params operator_api.SetTenantLogsParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation operator_api.SetTenantLogs has not yet been implemented")
|
||||
@@ -277,6 +281,8 @@ type OperatorAPI struct {
|
||||
OperatorAPIDeletePodHandler operator_api.DeletePodHandler
|
||||
// OperatorAPIDeleteTenantHandler sets the operation handler for the delete tenant operation
|
||||
OperatorAPIDeleteTenantHandler operator_api.DeleteTenantHandler
|
||||
// OperatorAPIDescribePodHandler sets the operation handler for the describe pod operation
|
||||
OperatorAPIDescribePodHandler operator_api.DescribePodHandler
|
||||
// OperatorAPIDisableTenantLoggingHandler sets the operation handler for the disable tenant logging operation
|
||||
OperatorAPIDisableTenantLoggingHandler operator_api.DisableTenantLoggingHandler
|
||||
// OperatorAPIEnableTenantLoggingHandler sets the operation handler for the enable tenant logging operation
|
||||
@@ -317,18 +323,18 @@ type OperatorAPI struct {
|
||||
OperatorAPIListPVCsForTenantHandler operator_api.ListPVCsForTenantHandler
|
||||
// OperatorAPIListTenantsHandler sets the operation handler for the list tenants operation
|
||||
OperatorAPIListTenantsHandler operator_api.ListTenantsHandler
|
||||
// UserAPILoginDetailHandler sets the operation handler for the login detail operation
|
||||
UserAPILoginDetailHandler user_api.LoginDetailHandler
|
||||
// UserAPILoginOauth2AuthHandler sets the operation handler for the login oauth2 auth operation
|
||||
UserAPILoginOauth2AuthHandler user_api.LoginOauth2AuthHandler
|
||||
// UserAPILoginOperatorHandler sets the operation handler for the login operator operation
|
||||
UserAPILoginOperatorHandler user_api.LoginOperatorHandler
|
||||
// UserAPILogoutHandler sets the operation handler for the logout operation
|
||||
UserAPILogoutHandler user_api.LogoutHandler
|
||||
// AuthLoginDetailHandler sets the operation handler for the login detail operation
|
||||
AuthLoginDetailHandler auth.LoginDetailHandler
|
||||
// AuthLoginOauth2AuthHandler sets the operation handler for the login oauth2 auth operation
|
||||
AuthLoginOauth2AuthHandler auth.LoginOauth2AuthHandler
|
||||
// AuthLoginOperatorHandler sets the operation handler for the login operator operation
|
||||
AuthLoginOperatorHandler auth.LoginOperatorHandler
|
||||
// AuthLogoutHandler sets the operation handler for the logout operation
|
||||
AuthLogoutHandler auth.LogoutHandler
|
||||
// OperatorAPIPutTenantYAMLHandler sets the operation handler for the put tenant y a m l operation
|
||||
OperatorAPIPutTenantYAMLHandler operator_api.PutTenantYAMLHandler
|
||||
// UserAPISessionCheckHandler sets the operation handler for the session check operation
|
||||
UserAPISessionCheckHandler user_api.SessionCheckHandler
|
||||
// AuthSessionCheckHandler sets the operation handler for the session check operation
|
||||
AuthSessionCheckHandler auth.SessionCheckHandler
|
||||
// OperatorAPISetTenantLogsHandler sets the operation handler for the set tenant logs operation
|
||||
OperatorAPISetTenantLogsHandler operator_api.SetTenantLogsHandler
|
||||
// OperatorAPISetTenantMonitoringHandler sets the operation handler for the set tenant monitoring operation
|
||||
@@ -466,6 +472,9 @@ func (o *OperatorAPI) Validate() error {
|
||||
if o.OperatorAPIDeleteTenantHandler == nil {
|
||||
unregistered = append(unregistered, "operator_api.DeleteTenantHandler")
|
||||
}
|
||||
if o.OperatorAPIDescribePodHandler == nil {
|
||||
unregistered = append(unregistered, "operator_api.DescribePodHandler")
|
||||
}
|
||||
if o.OperatorAPIDisableTenantLoggingHandler == nil {
|
||||
unregistered = append(unregistered, "operator_api.DisableTenantLoggingHandler")
|
||||
}
|
||||
@@ -526,23 +535,23 @@ func (o *OperatorAPI) Validate() error {
|
||||
if o.OperatorAPIListTenantsHandler == nil {
|
||||
unregistered = append(unregistered, "operator_api.ListTenantsHandler")
|
||||
}
|
||||
if o.UserAPILoginDetailHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.LoginDetailHandler")
|
||||
if o.AuthLoginDetailHandler == nil {
|
||||
unregistered = append(unregistered, "auth.LoginDetailHandler")
|
||||
}
|
||||
if o.UserAPILoginOauth2AuthHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.LoginOauth2AuthHandler")
|
||||
if o.AuthLoginOauth2AuthHandler == nil {
|
||||
unregistered = append(unregistered, "auth.LoginOauth2AuthHandler")
|
||||
}
|
||||
if o.UserAPILoginOperatorHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.LoginOperatorHandler")
|
||||
if o.AuthLoginOperatorHandler == nil {
|
||||
unregistered = append(unregistered, "auth.LoginOperatorHandler")
|
||||
}
|
||||
if o.UserAPILogoutHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.LogoutHandler")
|
||||
if o.AuthLogoutHandler == nil {
|
||||
unregistered = append(unregistered, "auth.LogoutHandler")
|
||||
}
|
||||
if o.OperatorAPIPutTenantYAMLHandler == nil {
|
||||
unregistered = append(unregistered, "operator_api.PutTenantYAMLHandler")
|
||||
}
|
||||
if o.UserAPISessionCheckHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.SessionCheckHandler")
|
||||
if o.AuthSessionCheckHandler == nil {
|
||||
unregistered = append(unregistered, "auth.SessionCheckHandler")
|
||||
}
|
||||
if o.OperatorAPISetTenantLogsHandler == nil {
|
||||
unregistered = append(unregistered, "operator_api.SetTenantLogsHandler")
|
||||
@@ -723,6 +732,10 @@ func (o *OperatorAPI) initHandlerCache() {
|
||||
o.handlers["DELETE"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["DELETE"]["/namespaces/{namespace}/tenants/{tenant}"] = operator_api.NewDeleteTenant(o.context, o.OperatorAPIDeleteTenantHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/namespaces/{namespace}/tenants/{tenant}/pods/{podName}/describe"] = operator_api.NewDescribePod(o.context, o.OperatorAPIDescribePodHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
@@ -806,19 +819,19 @@ func (o *OperatorAPI) initHandlerCache() {
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/login"] = user_api.NewLoginDetail(o.context, o.UserAPILoginDetailHandler)
|
||||
o.handlers["GET"]["/login"] = auth.NewLoginDetail(o.context, o.AuthLoginDetailHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/login/oauth2/auth"] = user_api.NewLoginOauth2Auth(o.context, o.UserAPILoginOauth2AuthHandler)
|
||||
o.handlers["POST"]["/login/oauth2/auth"] = auth.NewLoginOauth2Auth(o.context, o.AuthLoginOauth2AuthHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/login/operator"] = user_api.NewLoginOperator(o.context, o.UserAPILoginOperatorHandler)
|
||||
o.handlers["POST"]["/login/operator"] = auth.NewLoginOperator(o.context, o.AuthLoginOperatorHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/logout"] = user_api.NewLogout(o.context, o.UserAPILogoutHandler)
|
||||
o.handlers["POST"]["/logout"] = auth.NewLogout(o.context, o.AuthLogoutHandler)
|
||||
if o.handlers["PUT"] == nil {
|
||||
o.handlers["PUT"] = make(map[string]http.Handler)
|
||||
}
|
||||
@@ -826,7 +839,7 @@ func (o *OperatorAPI) initHandlerCache() {
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/session"] = user_api.NewSessionCheck(o.context, o.UserAPISessionCheckHandler)
|
||||
o.handlers["GET"]["/session"] = auth.NewSessionCheck(o.context, o.AuthSessionCheckHandler)
|
||||
if o.handlers["PUT"] == nil {
|
||||
o.handlers["PUT"] = make(map[string]http.Handler)
|
||||
}
|
||||
|
||||
88
operatorapi/operations/operator_api/describe_pod.go
Normal file
88
operatorapi/operations/operator_api/describe_pod.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 operator_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// DescribePodHandlerFunc turns a function with the right signature into a describe pod handler
|
||||
type DescribePodHandlerFunc func(DescribePodParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn DescribePodHandlerFunc) Handle(params DescribePodParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// DescribePodHandler interface for that can handle valid describe pod params
|
||||
type DescribePodHandler interface {
|
||||
Handle(DescribePodParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewDescribePod creates a new http.Handler for the describe pod operation
|
||||
func NewDescribePod(ctx *middleware.Context, handler DescribePodHandler) *DescribePod {
|
||||
return &DescribePod{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* DescribePod swagger:route GET /namespaces/{namespace}/tenants/{tenant}/pods/{podName}/describe OperatorAPI describePod
|
||||
|
||||
Describe Pod
|
||||
|
||||
*/
|
||||
type DescribePod struct {
|
||||
Context *middleware.Context
|
||||
Handler DescribePodHandler
|
||||
}
|
||||
|
||||
func (o *DescribePod) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewDescribePodParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
136
operatorapi/operations/operator_api/describe_pod_parameters.go
Normal file
136
operatorapi/operations/operator_api/describe_pod_parameters.go
Normal file
@@ -0,0 +1,136 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 operator_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
// NewDescribePodParams creates a new DescribePodParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewDescribePodParams() DescribePodParams {
|
||||
|
||||
return DescribePodParams{}
|
||||
}
|
||||
|
||||
// DescribePodParams contains all the bound params for the describe pod operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters DescribePod
|
||||
type DescribePodParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
Namespace string
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
PodName string
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
Tenant string
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewDescribePodParams() beforehand.
|
||||
func (o *DescribePodParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
rNamespace, rhkNamespace, _ := route.Params.GetOK("namespace")
|
||||
if err := o.bindNamespace(rNamespace, rhkNamespace, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
rPodName, rhkPodName, _ := route.Params.GetOK("podName")
|
||||
if err := o.bindPodName(rPodName, rhkPodName, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
rTenant, rhkTenant, _ := route.Params.GetOK("tenant")
|
||||
if err := o.bindTenant(rTenant, rhkTenant, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindNamespace binds and validates parameter Namespace from path.
|
||||
func (o *DescribePodParams) bindNamespace(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// Parameter is provided by construction from the route
|
||||
o.Namespace = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindPodName binds and validates parameter PodName from path.
|
||||
func (o *DescribePodParams) bindPodName(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// Parameter is provided by construction from the route
|
||||
o.PodName = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindTenant binds and validates parameter Tenant from path.
|
||||
func (o *DescribePodParams) bindTenant(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// Parameter is provided by construction from the route
|
||||
o.Tenant = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
133
operatorapi/operations/operator_api/describe_pod_responses.go
Normal file
133
operatorapi/operations/operator_api/describe_pod_responses.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 operator_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// DescribePodOKCode is the HTTP code returned for type DescribePodOK
|
||||
const DescribePodOKCode int = 200
|
||||
|
||||
/*DescribePodOK A successful response.
|
||||
|
||||
swagger:response describePodOK
|
||||
*/
|
||||
type DescribePodOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.DescribePodWrapper `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewDescribePodOK creates DescribePodOK with default headers values
|
||||
func NewDescribePodOK() *DescribePodOK {
|
||||
|
||||
return &DescribePodOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the describe pod o k response
|
||||
func (o *DescribePodOK) WithPayload(payload *models.DescribePodWrapper) *DescribePodOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the describe pod o k response
|
||||
func (o *DescribePodOK) SetPayload(payload *models.DescribePodWrapper) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *DescribePodOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*DescribePodDefault Generic error response.
|
||||
|
||||
swagger:response describePodDefault
|
||||
*/
|
||||
type DescribePodDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewDescribePodDefault creates DescribePodDefault with default headers values
|
||||
func NewDescribePodDefault(code int) *DescribePodDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &DescribePodDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the describe pod default response
|
||||
func (o *DescribePodDefault) WithStatusCode(code int) *DescribePodDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the describe pod default response
|
||||
func (o *DescribePodDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the describe pod default response
|
||||
func (o *DescribePodDefault) WithPayload(payload *models.Error) *DescribePodDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the describe pod default response
|
||||
func (o *DescribePodDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *DescribePodDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
132
operatorapi/operations/operator_api/describe_pod_urlbuilder.go
Normal file
132
operatorapi/operations/operator_api/describe_pod_urlbuilder.go
Normal file
@@ -0,0 +1,132 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 operator_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DescribePodURL generates an URL for the describe pod operation
|
||||
type DescribePodURL struct {
|
||||
Namespace string
|
||||
PodName string
|
||||
Tenant string
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *DescribePodURL) WithBasePath(bp string) *DescribePodURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *DescribePodURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *DescribePodURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/namespaces/{namespace}/tenants/{tenant}/pods/{podName}/describe"
|
||||
|
||||
namespace := o.Namespace
|
||||
if namespace != "" {
|
||||
_path = strings.Replace(_path, "{namespace}", namespace, -1)
|
||||
} else {
|
||||
return nil, errors.New("namespace is required on DescribePodURL")
|
||||
}
|
||||
|
||||
podName := o.PodName
|
||||
if podName != "" {
|
||||
_path = strings.Replace(_path, "{podName}", podName, -1)
|
||||
} else {
|
||||
return nil, errors.New("podName is required on DescribePodURL")
|
||||
}
|
||||
|
||||
tenant := o.Tenant
|
||||
if tenant != "" {
|
||||
_path = strings.Replace(_path, "{tenant}", tenant, -1)
|
||||
} else {
|
||||
return nil, errors.New("tenant is required on DescribePodURL")
|
||||
}
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *DescribePodURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *DescribePodURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *DescribePodURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on DescribePodURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on DescribePodURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *DescribePodURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -17,9 +17,10 @@
|
||||
package operatorapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/minio/console/restapi"
|
||||
errors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
@@ -50,14 +51,14 @@ func GetParityInfo(nodes int64, disksPerNode int64) (models.ParityResponse, erro
|
||||
}
|
||||
|
||||
func getParityResponse(params operator_api.GetParityParams) (models.ParityResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
nodes := params.Nodes
|
||||
disksPerNode := params.DisksPerNode
|
||||
|
||||
parityValues, err := GetParityInfo(nodes, disksPerNode)
|
||||
if err != nil {
|
||||
restapi.LogError("error getting parity info: %v", err)
|
||||
return nil, prepareError(err)
|
||||
errors.LogError("error getting parity info: %v", err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return parityValues, nil
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
xerrors "github.com/minio/console/restapi"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/minio/console/cluster"
|
||||
@@ -94,18 +96,18 @@ func getResourceQuota(ctx context.Context, client K8sClientI, namespace, resourc
|
||||
}
|
||||
|
||||
func getResourceQuotaResponse(session *models.Principal, params operator_api.GetResourceQuotaParams) (*models.ResourceQuota, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
client, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
k8sClient := &k8sClient{
|
||||
client: client,
|
||||
}
|
||||
resourceQuota, err := getResourceQuota(ctx, k8sClient, params.Namespace, params.ResourceQuotaName)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return resourceQuota, nil
|
||||
}
|
||||
|
||||
@@ -17,30 +17,35 @@
|
||||
package operatorapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
errors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations"
|
||||
"github.com/minio/console/operatorapi/operations/user_api"
|
||||
authApi "github.com/minio/console/operatorapi/operations/auth"
|
||||
)
|
||||
|
||||
func registerSessionHandlers(api *operations.OperatorAPI) {
|
||||
// session check
|
||||
api.UserAPISessionCheckHandler = user_api.SessionCheckHandlerFunc(func(params user_api.SessionCheckParams, session *models.Principal) middleware.Responder {
|
||||
sessionResp, err := getSessionResponse(session)
|
||||
api.AuthSessionCheckHandler = authApi.SessionCheckHandlerFunc(func(params authApi.SessionCheckParams, session *models.Principal) middleware.Responder {
|
||||
sessionResp, err := getSessionResponse(session, params)
|
||||
if err != nil {
|
||||
return user_api.NewSessionCheckDefault(int(err.Code)).WithPayload(err)
|
||||
return authApi.NewSessionCheckDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewSessionCheckOK().WithPayload(sessionResp)
|
||||
return authApi.NewSessionCheckOK().WithPayload(sessionResp)
|
||||
})
|
||||
}
|
||||
|
||||
// getSessionResponse parse the token of the current session and returns a list of allowed actions to render in the UI
|
||||
func getSessionResponse(session *models.Principal) (*models.OperatorSessionResponse, *models.Error) {
|
||||
func getSessionResponse(session *models.Principal, params authApi.SessionCheckParams) (*models.OperatorSessionResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// serialize output
|
||||
if session == nil {
|
||||
return nil, prepareError(errorGenericInvalidSession)
|
||||
return nil, errors.ErrorWithContext(ctx, errors.ErrInvalidSession)
|
||||
}
|
||||
sessionResp := &models.OperatorSessionResponse{
|
||||
Status: models.OperatorSessionResponseStatusOk,
|
||||
|
||||
@@ -41,7 +41,7 @@ import (
|
||||
func getTenantCreatedResponse(session *models.Principal, params operator_api.CreateTenantParams) (response *models.CreateTenantResponse, mError *models.Error) {
|
||||
tenantReq := params.Body
|
||||
minioImage := tenantReq.Image
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if minioImage == "" {
|
||||
minImg, err := cluster.GetMinioImage()
|
||||
@@ -56,7 +56,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
client: clientSet,
|
||||
}
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
ns := *tenantReq.Namespace
|
||||
@@ -98,7 +98,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
|
||||
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// Enable/Disable console object browser for MinIO tenant (default is on)
|
||||
@@ -110,7 +110,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
tenantConfigurationENV["MINIO_ROOT_USER"] = accessKey
|
||||
tenantConfigurationENV["MINIO_ROOT_PASSWORD"] = secretKey
|
||||
|
||||
// delete secrets created if an error occurred during tenant creation,
|
||||
// delete secrets created if an errors occurred during tenant creation,
|
||||
defer func() {
|
||||
if mError != nil {
|
||||
restapi.LogError("deleting secrets created for failed tenant: %s if any: %v", tenantName, mError)
|
||||
@@ -127,7 +127,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
// Check the Erasure Coding Parity for validity and pass it to Tenant
|
||||
if tenantReq.ErasureCodingParity > 0 {
|
||||
if tenantReq.ErasureCodingParity < 2 || tenantReq.ErasureCodingParity > 8 {
|
||||
return nil, prepareError(errorInvalidErasureCodingValue)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrInvalidErasureCodingValue)
|
||||
}
|
||||
tenantConfigurationENV["MINIO_STORAGE_CLASS_STANDARD"] = fmt.Sprintf("EC:%d", tenantReq.ErasureCodingParity)
|
||||
}
|
||||
@@ -204,7 +204,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
}
|
||||
_, err := clientSet.CoreV1().Secrets(ns).Create(ctx, &userSecret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
}
|
||||
// attach the users to the tenant
|
||||
@@ -248,7 +248,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
}
|
||||
_, err := clientSet.CoreV1().Secrets(ns).Create(ctx, &userSecret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
}
|
||||
// attach the users to the tenant
|
||||
@@ -274,7 +274,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
externalCertSecretName := fmt.Sprintf("%s-instance-external-certificates", secretName)
|
||||
externalCertSecret, err := createOrReplaceExternalCertSecrets(ctx, &k8sClient, ns, tenantReq.TLS.Minio, externalCertSecretName, tenantName)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
minInst.Spec.ExternalCertSecret = externalCertSecret
|
||||
}
|
||||
@@ -286,7 +286,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
certificates := []*models.KeyPairConfiguration{tenantReq.Encryption.Client}
|
||||
certificateSecrets, err := createOrReplaceExternalCertSecrets(ctx, &k8sClient, ns, certificates, tenantExternalClientCertSecretName, tenantName)
|
||||
if err != nil {
|
||||
return nil, prepareError(restapi.ErrorGeneric)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrDefault)
|
||||
}
|
||||
if len(certificateSecrets) > 0 {
|
||||
minInst.Spec.ExternalClientCertSecret = certificateSecrets[0]
|
||||
@@ -296,7 +296,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
// KES configuration for Tenant instance
|
||||
minInst.Spec.KES, err = getKESConfiguration(ctx, &k8sClient, ns, tenantReq.Encryption, secretName, tenantName)
|
||||
if err != nil {
|
||||
return nil, prepareError(restapi.ErrorGeneric)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrDefault)
|
||||
}
|
||||
// Set Labels, Annotations and Node Selector for KES
|
||||
minInst.Spec.KES.Labels = tenantReq.Encryption.Labels
|
||||
@@ -306,7 +306,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
if tenantReq.Encryption.SecurityContext != nil {
|
||||
sc, err := convertModelSCToK8sSC(tenantReq.Encryption.SecurityContext)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
minInst.Spec.KES.SecurityContext = sc
|
||||
}
|
||||
@@ -317,7 +317,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
for i, caCertificate := range tenantReq.TLS.CaCertificates {
|
||||
certificateContent, err := base64.StdEncoding.DecodeString(caCertificate)
|
||||
if err != nil {
|
||||
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrDefault, nil, err)
|
||||
}
|
||||
caCertificates = append(caCertificates, tenantSecret{
|
||||
Name: fmt.Sprintf("ca-certificate-%d", i),
|
||||
@@ -329,7 +329,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
if len(caCertificates) > 0 {
|
||||
certificateSecrets, err := createOrReplaceSecrets(ctx, &k8sClient, ns, caCertificates, tenantName)
|
||||
if err != nil {
|
||||
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrDefault, nil, err)
|
||||
}
|
||||
minInst.Spec.ExternalCaCertSecret = certificateSecrets
|
||||
}
|
||||
@@ -347,7 +347,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
pool, err := parseTenantPoolRequest(pool)
|
||||
if err != nil {
|
||||
restapi.LogError("parseTenantPoolRequest failed: %v", err)
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
minInst.Spec.Pools = append(minInst.Spec.Pools, *pool)
|
||||
}
|
||||
@@ -363,7 +363,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
if tenantReq.ImagePullSecret != "" {
|
||||
imagePullSecret = tenantReq.ImagePullSecret
|
||||
} else if imagePullSecret, err = setImageRegistry(ctx, tenantReq.ImageRegistry, clientSet.CoreV1(), ns, tenantName); err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// pass the image pull secret to the Tenant
|
||||
if imagePullSecret != "" {
|
||||
@@ -410,7 +410,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
if tenantReq.LogSearchConfiguration.SecurityContext != nil {
|
||||
sc, err := convertModelSCToK8sSC(tenantReq.LogSearchConfiguration.SecurityContext)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
logSearchSecurityContext = sc
|
||||
}
|
||||
@@ -418,7 +418,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
if tenantReq.LogSearchConfiguration.PostgresSecurityContext != nil {
|
||||
sc, err := convertModelSCToK8sSC(tenantReq.LogSearchConfiguration.PostgresSecurityContext)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
logSearchPgSecurityContext = sc
|
||||
}
|
||||
@@ -514,7 +514,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
if tenantReq.PrometheusConfiguration != nil && tenantReq.PrometheusConfiguration.SecurityContext != nil {
|
||||
sc, err := convertModelSCToK8sSC(tenantReq.PrometheusConfiguration.SecurityContext)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
minInst.Spec.Prometheus.SecurityContext = sc
|
||||
}
|
||||
@@ -538,7 +538,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
},
|
||||
}, tenantName)
|
||||
if err != nil {
|
||||
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrDefault, nil, err)
|
||||
}
|
||||
minInst.Spec.Configuration = &corev1.LocalObjectReference{Name: tenantConfigurationName}
|
||||
|
||||
@@ -562,20 +562,20 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
|
||||
opClient, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
_, err = opClient.MinioV2().Tenants(ns).Create(context.Background(), &minInst, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
restapi.LogError("Creating new tenant failed with: %v", err)
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// Integrations
|
||||
if os.Getenv("GKE_INTEGRATION") != "" {
|
||||
err := gkeIntegration(clientSet, tenantName, ns, session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
}
|
||||
response = &models.CreateTenantResponse{
|
||||
|
||||
@@ -20,20 +20,21 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
errors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
"github.com/minio/console/restapi"
|
||||
miniov2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func getTenantDetailsResponse(session *models.Principal, params operator_api.TenantDetailsParams) (*models.Tenant, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
@@ -42,7 +43,7 @@ func getTenantDetailsResponse(session *models.Principal, params operator_api.Ten
|
||||
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
info := getTenantInfo(minTenant)
|
||||
@@ -50,7 +51,7 @@ func getTenantDetailsResponse(session *models.Principal, params operator_api.Ten
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
k8sClient := k8sClient{
|
||||
@@ -59,7 +60,7 @@ func getTenantDetailsResponse(session *models.Principal, params operator_api.Ten
|
||||
|
||||
tenantConfiguration, err := GetTenantConfiguration(ctx, &k8sClient, minTenant)
|
||||
if err != nil {
|
||||
restapi.LogError("unable to fetch configuration for tenant %s: %v", minTenant.Name, err)
|
||||
errors.LogError("unable to fetch configuration for tenant %s: %v", minTenant.Name, err)
|
||||
}
|
||||
|
||||
// detect if AD/LDAP is enabled
|
||||
@@ -105,14 +106,14 @@ func getTenantDetailsResponse(session *models.Principal, params operator_api.Ten
|
||||
//minio service
|
||||
minSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.MinIOCIServiceName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// we can tolerate this error
|
||||
restapi.LogError("Unable to get MinIO service name: %v, continuing", err)
|
||||
// we can tolerate this errors
|
||||
errors.LogError("Unable to get MinIO service name: %v, continuing", err)
|
||||
}
|
||||
//console service
|
||||
conSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.ConsoleCIServiceName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// we can tolerate this error
|
||||
restapi.LogError("Unable to get MinIO console service name: %v, continuing", err)
|
||||
// we can tolerate this errors
|
||||
errors.LogError("Unable to get MinIO console service name: %v, continuing", err)
|
||||
}
|
||||
|
||||
schema := "http"
|
||||
|
||||
@@ -22,9 +22,12 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/console/restapi"
|
||||
|
||||
"github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
utils2 "github.com/minio/console/pkg/utils"
|
||||
"github.com/minio/console/restapi"
|
||||
miniov2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
@@ -32,7 +35,7 @@ import (
|
||||
)
|
||||
|
||||
// updateTenantAction does an update on the minioTenant by patching the desired changes
|
||||
func updateTenantAction(ctx context.Context, operatorClient OperatorClientI, clientset v1.CoreV1Interface, httpCl utils2.HTTPClientI, namespace string, params operator_api.UpdateTenantParams) error {
|
||||
func updateTenantAction(ctx context.Context, operatorClient OperatorClientI, clientset v1.CoreV1Interface, httpCl http.ClientI, namespace string, params operator_api.UpdateTenantParams) error {
|
||||
imageToUpdate := params.Body.Image
|
||||
imageRegistryReq := params.Body.ImageRegistry
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,8 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
xerrors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
|
||||
"errors"
|
||||
@@ -108,25 +110,25 @@ func tenantUpdateCertificates(ctx context.Context, operatorClient OperatorClient
|
||||
|
||||
// getTenantUpdateCertificatesResponse wrapper of tenantUpdateCertificates
|
||||
func getTenantUpdateCertificatesResponse(session *models.Principal, params operator_api.TenantUpdateCertificateParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err, errorUnableToUpdateTenantCertificates)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrUnableToUpdateTenantCertificates)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err, errorUnableToUpdateTenantCertificates)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrUnableToUpdateTenantCertificates)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err := tenantUpdateCertificates(ctx, &opClient, &k8sClient, params.Namespace, params); err != nil {
|
||||
return prepareError(err, errorUnableToUpdateTenantCertificates)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrUnableToUpdateTenantCertificates)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -239,42 +241,42 @@ func tenantUpdateEncryption(ctx context.Context, operatorClient OperatorClientI,
|
||||
|
||||
// getTenantDeleteEncryptionResponse is a wrapper for tenantDeleteEncryption
|
||||
func getTenantDeleteEncryptionResponse(session *models.Principal, params operator_api.TenantDeleteEncryptionParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err, errorDeletingEncryptionConfig)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrDeletingEncryptionConfig)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err := tenantDeleteEncryption(ctx, &opClient, params.Namespace, params); err != nil {
|
||||
return prepareError(err, errorDeletingEncryptionConfig)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrDeletingEncryptionConfig)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getTenantUpdateEncryptionResponse is a wrapper for tenantUpdateEncryption
|
||||
func getTenantUpdateEncryptionResponse(session *models.Principal, params operator_api.TenantUpdateEncryptionParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err, errorUpdatingEncryptionConfig)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrUpdatingEncryptionConfig)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err, errorUpdatingEncryptionConfig)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrUpdatingEncryptionConfig)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err := tenantUpdateEncryption(ctx, &opClient, &k8sClient, params.Namespace, params); err != nil {
|
||||
return prepareError(err, errorUpdatingEncryptionConfig)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrUpdatingEncryptionConfig)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -453,26 +455,26 @@ func tenantEncryptionInfo(ctx context.Context, operatorClient OperatorClientI, c
|
||||
|
||||
// getTenantEncryptionResponse is a wrapper for tenantEncryptionInfo
|
||||
func getTenantEncryptionInfoResponse(session *models.Principal, params operator_api.TenantEncryptionInfoParams) (*models.EncryptionConfigurationResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorEncryptionConfigNotFound)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err, xerrors.ErrEncryptionConfigNotFound)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorEncryptionConfigNotFound)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err, xerrors.ErrEncryptionConfigNotFound)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
configuration, err := tenantEncryptionInfo(ctx, &opClient, &k8sClient, params.Namespace, params)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorEncryptionConfigNotFound)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err, xerrors.ErrEncryptionConfigNotFound)
|
||||
}
|
||||
return configuration, nil
|
||||
}
|
||||
@@ -541,8 +543,8 @@ func createOrReplaceSecrets(ctx context.Context, clientSet K8sClientI, ns string
|
||||
// delete secret with same name if exists
|
||||
err := clientSet.deleteSecret(ctx, ns, secret.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
// log the error if any and continue
|
||||
LogError("deleting secret name %s failed: %v, continuing..", secret.Name, err)
|
||||
// log the errors if any and continue
|
||||
xerrors.LogError("deleting secret name %s failed: %v, continuing..", secret.Name, err)
|
||||
}
|
||||
k8sSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -579,8 +581,8 @@ func createOrReplaceExternalCertSecrets(ctx context.Context, clientSet K8sClient
|
||||
// delete secret with same name if exists
|
||||
err := clientSet.deleteSecret(ctx, ns, keyPairSecretName, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
// log the error if any and continue
|
||||
LogError("deleting secret name %s failed: %v, continuing..", keyPairSecretName, err)
|
||||
// log the errors if any and continue
|
||||
xerrors.LogError("deleting secret name %s failed: %v, continuing..", keyPairSecretName, err)
|
||||
}
|
||||
imm := true
|
||||
tlsCrt, err := base64.StdEncoding.DecodeString(*keyPair.Crt)
|
||||
@@ -625,13 +627,13 @@ func createOrReplaceExternalCertSecrets(ctx context.Context, clientSet K8sClient
|
||||
func createOrReplaceKesConfigurationSecrets(ctx context.Context, clientSet K8sClientI, ns string, encryptionCfg *models.EncryptionConfiguration, kesConfigurationSecretName, kesClientCertSecretName, tenantName string) (*corev1.LocalObjectReference, *miniov2.LocalCertificateReference, error) {
|
||||
// delete KES configuration secret if exists
|
||||
if err := clientSet.deleteSecret(ctx, ns, kesConfigurationSecretName, metav1.DeleteOptions{}); err != nil {
|
||||
// log the error if any and continue
|
||||
LogError("deleting secret name %s failed: %v, continuing..", kesConfigurationSecretName, err)
|
||||
// log the errors if any and continue
|
||||
xerrors.LogError("deleting secret name %s failed: %v, continuing..", kesConfigurationSecretName, err)
|
||||
}
|
||||
// delete KES client cert secret if exists
|
||||
if err := clientSet.deleteSecret(ctx, ns, kesClientCertSecretName, metav1.DeleteOptions{}); err != nil {
|
||||
// log the error if any and continue
|
||||
LogError("deleting secret name %s failed: %v, continuing..", kesClientCertSecretName, err)
|
||||
// log the errors if any and continue
|
||||
xerrors.LogError("deleting secret name %s failed: %v, continuing..", kesClientCertSecretName, err)
|
||||
}
|
||||
// if autoCert is enabled then Operator will generate the client certificates, calculate the client cert identity
|
||||
// and pass it to KES via the ${MINIO_KES_IDENTITY} variable
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
|
||||
@@ -897,7 +897,7 @@ func Test_UpdateTenantAction(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
operatorClient OperatorClientI
|
||||
httpCl utils.HTTPClientI
|
||||
httpCl xhttp.ClientI
|
||||
nameSpace string
|
||||
tenantName string
|
||||
mockTenantPatch func(ctx context.Context, namespace string, tenantName string, pt types.PatchType, data []byte, options metav1.PatchOptions) (*miniov2.Tenant, error)
|
||||
|
||||
@@ -17,20 +17,24 @@
|
||||
package operatorapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
errors "github.com/minio/console/restapi"
|
||||
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations"
|
||||
"github.com/minio/console/operatorapi/operations/user_api"
|
||||
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
)
|
||||
|
||||
func registerVersionHandlers(api *operations.OperatorAPI) {
|
||||
api.UserAPICheckMinIOVersionHandler = user_api.CheckMinIOVersionHandlerFunc(func(params user_api.CheckMinIOVersionParams) middleware.Responder {
|
||||
versionResponse, err := getVersionResponse()
|
||||
versionResponse, err := getVersionResponse(params)
|
||||
if err != nil {
|
||||
return user_api.NewCheckMinIOVersionDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -39,13 +43,15 @@ func registerVersionHandlers(api *operations.OperatorAPI) {
|
||||
}
|
||||
|
||||
// getSessionResponse parse the token of the current session and returns a list of allowed actions to render in the UI
|
||||
func getVersionResponse() (*models.CheckOperatorVersionResponse, *models.Error) {
|
||||
ver, err := utils.GetLatestMinIOImage(&utils.HTTPClient{
|
||||
func getVersionResponse(params user_api.CheckMinIOVersionParams) (*models.CheckOperatorVersionResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
ver, err := utils.GetLatestMinIOImage(&xhttp.Client{
|
||||
Client: &http.Client{
|
||||
Timeout: 15 * time.Second,
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.CheckOperatorVersionResponse{
|
||||
LatestVersion: *ver,
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
errors "github.com/minio/console/restapi"
|
||||
|
||||
miniov1 "github.com/minio/operator/pkg/apis/minio.min.io/v1"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
@@ -33,7 +35,7 @@ import (
|
||||
|
||||
func registerVolumesHandlers(api *operations.OperatorAPI) {
|
||||
api.OperatorAPIListPVCsHandler = operator_api.ListPVCsHandlerFunc(func(params operator_api.ListPVCsParams, session *models.Principal) middleware.Responder {
|
||||
payload, err := getPVCsResponse(session)
|
||||
payload, err := getPVCsResponse(session, params)
|
||||
|
||||
if err != nil {
|
||||
return operator_api.NewListPVCsDefault(int(err.Code)).WithPayload(err)
|
||||
@@ -72,13 +74,13 @@ func registerVolumesHandlers(api *operations.OperatorAPI) {
|
||||
|
||||
}
|
||||
|
||||
func getPVCsResponse(session *models.Principal) (*models.ListPVCsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getPVCsResponse(session *models.Principal, params operator_api.ListPVCsParams) (*models.ListPVCsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// Filter Tenant PVCs. They keep their v1 tenant annotation
|
||||
@@ -90,7 +92,7 @@ func getPVCsResponse(session *models.Principal) (*models.ListPVCsResponse, *mode
|
||||
listAllPvcs, err2 := clientset.CoreV1().PersistentVolumeClaims("").List(ctx, listOpts)
|
||||
|
||||
if err2 != nil {
|
||||
return nil, prepareError(err2)
|
||||
return nil, errors.ErrorWithContext(ctx, err2)
|
||||
}
|
||||
|
||||
var ListPVCs []*models.PvcsListResponse
|
||||
@@ -121,12 +123,12 @@ func getPVCsResponse(session *models.Principal) (*models.ListPVCsResponse, *mode
|
||||
}
|
||||
|
||||
func getPVCsForTenantResponse(session *models.Principal, params operator_api.ListPVCsForTenantParams) (*models.ListPVCsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// Filter Tenant PVCs. They keep their v1 tenant annotation
|
||||
@@ -138,7 +140,7 @@ func getPVCsForTenantResponse(session *models.Principal, params operator_api.Lis
|
||||
listAllPvcs, err2 := clientset.CoreV1().PersistentVolumeClaims(params.Namespace).List(ctx, listOpts)
|
||||
|
||||
if err2 != nil {
|
||||
return nil, prepareError(err2)
|
||||
return nil, errors.ErrorWithContext(ctx, err2)
|
||||
}
|
||||
|
||||
var ListPVCs []*models.PvcsListResponse
|
||||
@@ -169,37 +171,37 @@ func getPVCsForTenantResponse(session *models.Principal, params operator_api.Lis
|
||||
}
|
||||
|
||||
func getDeletePVCResponse(session *models.Principal, params operator_api.DeletePVCParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// get Kubernetes Client
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
listOpts := metav1.ListOptions{
|
||||
LabelSelector: fmt.Sprintf("v1.min.io/tenant=%s", params.Tenant),
|
||||
FieldSelector: fmt.Sprintf("metadata.name=%s", params.PVCName),
|
||||
}
|
||||
if err = clientset.CoreV1().PersistentVolumeClaims(params.Namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOpts); err != nil {
|
||||
return prepareError(err)
|
||||
return errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPVCEventsResponse(session *models.Principal, params operator_api.GetPVCEventsParams) (models.EventListWrapper, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
PVC, err := clientset.CoreV1().PersistentVolumeClaims(params.Namespace).Get(ctx, params.PVCName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
events, err := clientset.CoreV1().Events(params.Namespace).List(ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("involvedObject.uid=%s", PVC.UID)})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
retval := models.EventListWrapper{}
|
||||
for i := 0; i < len(events.Items); i++ {
|
||||
|
||||
@@ -89,11 +89,14 @@ func SessionTokenAuthenticate(token string) (*TokenClaims, error) {
|
||||
if token == "" {
|
||||
return nil, ErrNoAuthToken
|
||||
}
|
||||
// decrypt encrypted token
|
||||
claimTokens, err := decryptClaims(token)
|
||||
decryptedToken, err := DecryptToken(token)
|
||||
if err != nil {
|
||||
// we print decryption token error information for debugging purposes
|
||||
// we return a generic error that doesn't give any information to attackers
|
||||
// fail decrypting token
|
||||
return nil, errReadingToken
|
||||
}
|
||||
claimTokens, err := ParseClaimsFromToken(string(decryptedToken))
|
||||
if err != nil {
|
||||
// fail unmarshalling token into data structure
|
||||
return nil, errReadingToken
|
||||
}
|
||||
// claimsTokens contains the decrypted JWT for Console
|
||||
@@ -136,21 +139,26 @@ func encryptClaims(credentials *TokenClaims) (string, error) {
|
||||
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||
}
|
||||
|
||||
// decryptClaims() receives base64 encoded ciphertext, decode it, decrypt it (AES-GCM) and produces a *TokenClaims object
|
||||
func decryptClaims(ciphertext string) (*TokenClaims, error) {
|
||||
// ParseClaimsFromToken receive token claims in string format, then unmarshal them to produce a *TokenClaims object
|
||||
func ParseClaimsFromToken(claims string) (*TokenClaims, error) {
|
||||
tokenClaims := &TokenClaims{}
|
||||
if err := json.Unmarshal([]byte(claims), tokenClaims); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tokenClaims, nil
|
||||
}
|
||||
|
||||
// DecryptToken receives base64 encoded ciphertext, decode it, decrypt it (AES-GCM) and produces []byte
|
||||
func DecryptToken(ciphertext string) (plaintext []byte, err error) {
|
||||
decoded, err := base64.StdEncoding.DecodeString(ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plaintext, err := decrypt(decoded, []byte{})
|
||||
plaintext, err = decrypt(decoded, []byte{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenClaims := &TokenClaims{}
|
||||
if err = json.Unmarshal(plaintext, tokenClaims); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tokenClaims, nil
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -332,3 +332,12 @@ func GetAllCertificatesAndCAs() (*x509.CertPool, []*x509.Certificate, *xcerts.Ma
|
||||
}
|
||||
return rootCAs, publicCerts, certsManager, nil
|
||||
}
|
||||
|
||||
// EnsureCertAndKey checks if both client certificate and key paths are provided
|
||||
func EnsureCertAndKey(clientCert, clientKey string) error {
|
||||
if (clientCert != "" && clientKey == "") ||
|
||||
(clientCert == "" && clientKey != "") {
|
||||
return errors.New("cert and key must be specified as a pair")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
23
pkg/http/headers.go
Normal file
23
pkg/http/headers.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 http
|
||||
|
||||
// Standard S3 HTTP response constants
|
||||
const (
|
||||
ETag = "ETag"
|
||||
ContentType = "Content-Type"
|
||||
)
|
||||
@@ -14,40 +14,61 @@
|
||||
// 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 utils
|
||||
package http
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// HTTPClientI interface with all functions to be implemented
|
||||
// ClientI interface with all functions to be implemented
|
||||
// by mock when testing, it should include all HttpClient respective api calls
|
||||
// that are used within this project.
|
||||
type HTTPClientI interface {
|
||||
type ClientI interface {
|
||||
Get(url string) (resp *http.Response, err error)
|
||||
Post(url, contentType string, body io.Reader) (resp *http.Response, err error)
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// HTTPClient Interface implementation
|
||||
// Client is an HTTP Interface implementation
|
||||
//
|
||||
// Define the structure of a http client and define the functions that are actually used
|
||||
type HTTPClient struct {
|
||||
type Client struct {
|
||||
Client *http.Client
|
||||
}
|
||||
|
||||
// Get implements http.Client.Get()
|
||||
func (c *HTTPClient) Get(url string) (resp *http.Response, err error) {
|
||||
func (c *Client) Get(url string) (resp *http.Response, err error) {
|
||||
return c.Client.Get(url)
|
||||
}
|
||||
|
||||
// Post implements http.Client.Post()
|
||||
func (c *HTTPClient) Post(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
func (c *Client) Post(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
return c.Client.Post(url, contentType, body)
|
||||
}
|
||||
|
||||
// Do implement http.Client.Do()
|
||||
func (c *HTTPClient) Do(req *http.Request) (*http.Response, error) {
|
||||
func (c *Client) Do(req *http.Request) (*http.Response, error) {
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
// DrainBody close non nil response with any response Body.
|
||||
// convenient wrapper to drain any remaining data on response body.
|
||||
//
|
||||
// Subsequently this allows golang http RoundTripper
|
||||
// to re-use the same connection for future requests.
|
||||
func DrainBody(respBody io.ReadCloser) {
|
||||
// Callers should close resp.Body when done reading from it.
|
||||
// If resp.Body is not closed, the Client's underlying RoundTripper
|
||||
// (typically Transport) may not be able to re-use a persistent TCP
|
||||
// connection to the server for a subsequent "keep-alive" request.
|
||||
if respBody != nil {
|
||||
// Drain any remaining Body and then close the connection.
|
||||
// Without this closing connection would disallow re-using
|
||||
// the same connection for future uses.
|
||||
// - http://stackoverflow.com/a/17961593/4465767
|
||||
defer respBody.Close()
|
||||
io.Copy(ioutil.Discard, respBody)
|
||||
}
|
||||
}
|
||||
228
pkg/logger/audit.go
Normal file
228
pkg/logger/audit.go
Normal file
@@ -0,0 +1,228 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 logger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/minio/console/pkg/logger/message/audit"
|
||||
)
|
||||
|
||||
// ResponseWriter - is a wrapper to trap the http response status code.
|
||||
type ResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
StatusCode int
|
||||
// Log body of 4xx or 5xx responses
|
||||
LogErrBody bool
|
||||
// Log body of all responses
|
||||
LogAllBody bool
|
||||
|
||||
TimeToFirstByte time.Duration
|
||||
StartTime time.Time
|
||||
// number of bytes written
|
||||
bytesWritten int
|
||||
// Internal recording buffer
|
||||
headers bytes.Buffer
|
||||
body bytes.Buffer
|
||||
// Indicate if headers are written in the log
|
||||
headersLogged bool
|
||||
}
|
||||
|
||||
// NewResponseWriter - returns a wrapped response writer to trap
|
||||
// http status codes for auditing purposes.
|
||||
func NewResponseWriter(w http.ResponseWriter) *ResponseWriter {
|
||||
return &ResponseWriter{
|
||||
ResponseWriter: w,
|
||||
StatusCode: http.StatusOK,
|
||||
StartTime: time.Now().UTC(),
|
||||
}
|
||||
}
|
||||
|
||||
func (lrw *ResponseWriter) Write(p []byte) (int, error) {
|
||||
if !lrw.headersLogged {
|
||||
// We assume the response code to be '200 OK' when WriteHeader() is not called,
|
||||
// that way following Golang HTTP response behavior.
|
||||
lrw.WriteHeader(http.StatusOK)
|
||||
}
|
||||
n, err := lrw.ResponseWriter.Write(p)
|
||||
lrw.bytesWritten += n
|
||||
if lrw.TimeToFirstByte == 0 {
|
||||
lrw.TimeToFirstByte = time.Now().UTC().Sub(lrw.StartTime)
|
||||
}
|
||||
if (lrw.LogErrBody && lrw.StatusCode >= http.StatusBadRequest) || lrw.LogAllBody {
|
||||
// Always logging error responses.
|
||||
lrw.body.Write(p)
|
||||
}
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Write the headers into the given buffer
|
||||
func (lrw *ResponseWriter) writeHeaders(w io.Writer, statusCode int, headers http.Header) {
|
||||
n, _ := fmt.Fprintf(w, "%d %s\n", statusCode, http.StatusText(statusCode))
|
||||
lrw.bytesWritten += n
|
||||
for k, v := range headers {
|
||||
n, _ := fmt.Fprintf(w, "%s: %s\n", k, v[0])
|
||||
lrw.bytesWritten += n
|
||||
}
|
||||
}
|
||||
|
||||
// BodyPlaceHolder returns a dummy body placeholder
|
||||
var BodyPlaceHolder = []byte("<BODY>")
|
||||
|
||||
// Body - Return response body.
|
||||
func (lrw *ResponseWriter) Body() []byte {
|
||||
// If there was an error response or body logging is enabled
|
||||
// then we return the body contents
|
||||
if (lrw.LogErrBody && lrw.StatusCode >= http.StatusBadRequest) || lrw.LogAllBody {
|
||||
return lrw.body.Bytes()
|
||||
}
|
||||
// ... otherwise we return the <BODY> place holder
|
||||
return BodyPlaceHolder
|
||||
}
|
||||
|
||||
// WriteHeader - writes http status code
|
||||
func (lrw *ResponseWriter) WriteHeader(code int) {
|
||||
if !lrw.headersLogged {
|
||||
lrw.StatusCode = code
|
||||
lrw.writeHeaders(&lrw.headers, code, lrw.ResponseWriter.Header())
|
||||
lrw.headersLogged = true
|
||||
lrw.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
}
|
||||
|
||||
// Flush - Calls the underlying Flush.
|
||||
func (lrw *ResponseWriter) Flush() {
|
||||
lrw.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
// Size - reutrns the number of bytes written
|
||||
func (lrw *ResponseWriter) Size() int {
|
||||
return lrw.bytesWritten
|
||||
}
|
||||
|
||||
// SetAuditEntry sets Audit info in the context.
|
||||
func SetAuditEntry(ctx context.Context, audit *audit.Entry) context.Context {
|
||||
if ctx == nil {
|
||||
LogIf(context.Background(), fmt.Errorf("context is nil"))
|
||||
return nil
|
||||
}
|
||||
return context.WithValue(ctx, utils.ContextAuditKey, audit)
|
||||
}
|
||||
|
||||
// GetAuditEntry returns Audit entry if set.
|
||||
func GetAuditEntry(ctx context.Context) *audit.Entry {
|
||||
if ctx != nil {
|
||||
r, ok := ctx.Value(utils.ContextAuditKey).(*audit.Entry)
|
||||
if ok {
|
||||
return r
|
||||
}
|
||||
r = &audit.Entry{
|
||||
Version: audit.Version,
|
||||
//DeploymentID: globalDeploymentID,
|
||||
Time: time.Now().UTC(),
|
||||
}
|
||||
SetAuditEntry(ctx, r)
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AuditLog - logs audit logs to all audit targets.
|
||||
func AuditLog(ctx context.Context, w *ResponseWriter, r *http.Request, reqClaims map[string]interface{}, filterKeys ...string) {
|
||||
// Fast exit if there is not audit target configured
|
||||
if atomic.LoadInt32(&nAuditTargets) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var entry audit.Entry
|
||||
|
||||
if w != nil && r != nil {
|
||||
reqInfo := GetReqInfo(ctx)
|
||||
if reqInfo == nil {
|
||||
return
|
||||
}
|
||||
entry = audit.ToEntry(w, r, reqClaims, GetGlobalDeploymentID())
|
||||
// indicates all requests for this API call are inbound
|
||||
entry.Trigger = "incoming"
|
||||
|
||||
for _, filterKey := range filterKeys {
|
||||
delete(entry.ReqClaims, filterKey)
|
||||
delete(entry.ReqQuery, filterKey)
|
||||
delete(entry.ReqHeader, filterKey)
|
||||
delete(entry.RespHeader, filterKey)
|
||||
}
|
||||
|
||||
var (
|
||||
statusCode int
|
||||
timeToResponse time.Duration
|
||||
timeToFirstByte time.Duration
|
||||
outputBytes int64 = -1 // -1: unknown output bytes
|
||||
)
|
||||
|
||||
if w != nil {
|
||||
statusCode = w.StatusCode
|
||||
timeToResponse = time.Now().UTC().Sub(w.StartTime)
|
||||
timeToFirstByte = w.TimeToFirstByte
|
||||
outputBytes = int64(w.Size())
|
||||
}
|
||||
|
||||
entry.API.Path = r.URL.Path
|
||||
|
||||
entry.API.Status = http.StatusText(statusCode)
|
||||
entry.API.StatusCode = statusCode
|
||||
entry.API.Method = r.Method
|
||||
entry.API.InputBytes = r.ContentLength
|
||||
entry.API.OutputBytes = outputBytes
|
||||
entry.RequestID = reqInfo.RequestID
|
||||
|
||||
entry.API.TimeToResponse = strconv.FormatInt(timeToResponse.Nanoseconds(), 10) + "ns"
|
||||
entry.Tags = reqInfo.GetTagsMap()
|
||||
// ttfb will be recorded only for GET requests, Ignore such cases where ttfb will be empty.
|
||||
if timeToFirstByte != 0 {
|
||||
entry.API.TimeToFirstByte = strconv.FormatInt(timeToFirstByte.Nanoseconds(), 10) + "ns"
|
||||
}
|
||||
} else {
|
||||
auditEntry := GetAuditEntry(ctx)
|
||||
if auditEntry != nil {
|
||||
entry = *auditEntry
|
||||
}
|
||||
}
|
||||
|
||||
if anonFlag {
|
||||
entry.SessionID = hashString(entry.SessionID)
|
||||
entry.RemoteHost = hashString(entry.RemoteHost)
|
||||
}
|
||||
|
||||
// Send audit logs only to http targets.
|
||||
for _, t := range AuditTargets() {
|
||||
if err := t.Send(entry, string(All)); err != nil {
|
||||
LogAlwaysIf(context.Background(), fmt.Errorf("event(%v) was not sent to Audit target (%v): %v", entry, t, err), All)
|
||||
}
|
||||
}
|
||||
}
|
||||
60
pkg/logger/color/color.go
Normal file
60
pkg/logger/color/color.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
// global colors.
|
||||
var (
|
||||
// Check if we stderr, stdout are dumb terminals, we do not apply
|
||||
// ansi coloring on dumb terminals.
|
||||
IsTerminal = func() bool {
|
||||
return !color.NoColor
|
||||
}
|
||||
|
||||
Bold = func() func(a ...interface{}) string {
|
||||
if IsTerminal() {
|
||||
return color.New(color.Bold).SprintFunc()
|
||||
}
|
||||
return fmt.Sprint
|
||||
}()
|
||||
|
||||
FgRed = func() func(a ...interface{}) string {
|
||||
if IsTerminal() {
|
||||
return color.New(color.FgRed).SprintFunc()
|
||||
}
|
||||
return fmt.Sprint
|
||||
}()
|
||||
|
||||
BgRed = func() func(format string, a ...interface{}) string {
|
||||
if IsTerminal() {
|
||||
return color.New(color.BgRed).SprintfFunc()
|
||||
}
|
||||
return fmt.Sprintf
|
||||
}()
|
||||
|
||||
FgWhite = func() func(format string, a ...interface{}) string {
|
||||
if IsTerminal() {
|
||||
return color.New(color.FgWhite).SprintfFunc()
|
||||
}
|
||||
return fmt.Sprintf
|
||||
}()
|
||||
)
|
||||
213
pkg/logger/config.go
Normal file
213
pkg/logger/config.go
Normal file
@@ -0,0 +1,213 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 logger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/minio/console/pkg/logger/config"
|
||||
"github.com/minio/console/pkg/logger/target/http"
|
||||
"github.com/minio/pkg/env"
|
||||
)
|
||||
|
||||
// NewConfig - initialize new logger config.
|
||||
func NewConfig() Config {
|
||||
cfg := Config{
|
||||
HTTP: make(map[string]http.Config),
|
||||
AuditWebhook: make(map[string]http.Config),
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func lookupLoggerWebhookConfig() (Config, error) {
|
||||
cfg := NewConfig()
|
||||
envs := env.List(EnvLoggerWebhookEndpoint)
|
||||
var loggerTargets []string
|
||||
for _, k := range envs {
|
||||
target := strings.TrimPrefix(k, EnvLoggerWebhookEndpoint+config.Default)
|
||||
if target == EnvLoggerWebhookEndpoint {
|
||||
target = config.Default
|
||||
}
|
||||
loggerTargets = append(loggerTargets, target)
|
||||
}
|
||||
|
||||
// Load HTTP logger from the environment if found
|
||||
for _, target := range loggerTargets {
|
||||
if v, ok := cfg.HTTP[target]; ok && v.Enabled {
|
||||
// This target is already enabled using the
|
||||
// legacy environment variables, ignore.
|
||||
continue
|
||||
}
|
||||
enableEnv := EnvLoggerWebhookEnable
|
||||
if target != config.Default {
|
||||
enableEnv = EnvLoggerWebhookEnable + config.Default + target
|
||||
}
|
||||
enable, err := config.ParseBool(env.Get(enableEnv, ""))
|
||||
if err != nil || !enable {
|
||||
continue
|
||||
}
|
||||
endpointEnv := EnvLoggerWebhookEndpoint
|
||||
if target != config.Default {
|
||||
endpointEnv = EnvLoggerWebhookEndpoint + config.Default + target
|
||||
}
|
||||
authTokenEnv := EnvLoggerWebhookAuthToken
|
||||
if target != config.Default {
|
||||
authTokenEnv = EnvLoggerWebhookAuthToken + config.Default + target
|
||||
}
|
||||
clientCertEnv := EnvLoggerWebhookClientCert
|
||||
if target != config.Default {
|
||||
clientCertEnv = EnvLoggerWebhookClientCert + config.Default + target
|
||||
}
|
||||
clientKeyEnv := EnvLoggerWebhookClientKey
|
||||
if target != config.Default {
|
||||
clientKeyEnv = EnvLoggerWebhookClientKey + config.Default + target
|
||||
}
|
||||
err = config.EnsureCertAndKey(env.Get(clientCertEnv, ""), env.Get(clientKeyEnv, ""))
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
queueSizeEnv := EnvLoggerWebhookQueueSize
|
||||
if target != config.Default {
|
||||
queueSizeEnv = EnvLoggerWebhookQueueSize + config.Default + target
|
||||
}
|
||||
queueSize, err := strconv.Atoi(env.Get(queueSizeEnv, "100000"))
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
if queueSize <= 0 {
|
||||
return cfg, errors.New("invalid queue_size value")
|
||||
}
|
||||
cfg.HTTP[target] = http.Config{
|
||||
Enabled: true,
|
||||
Endpoint: env.Get(endpointEnv, ""),
|
||||
AuthToken: env.Get(authTokenEnv, ""),
|
||||
ClientCert: env.Get(clientCertEnv, ""),
|
||||
ClientKey: env.Get(clientKeyEnv, ""),
|
||||
QueueSize: queueSize,
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func lookupAuditWebhookConfig() (Config, error) {
|
||||
cfg := NewConfig()
|
||||
var loggerAuditTargets []string
|
||||
envs := env.List(EnvAuditWebhookEndpoint)
|
||||
for _, k := range envs {
|
||||
target := strings.TrimPrefix(k, EnvAuditWebhookEndpoint+config.Default)
|
||||
if target == EnvAuditWebhookEndpoint {
|
||||
target = config.Default
|
||||
}
|
||||
loggerAuditTargets = append(loggerAuditTargets, target)
|
||||
}
|
||||
|
||||
for _, target := range loggerAuditTargets {
|
||||
if v, ok := cfg.AuditWebhook[target]; ok && v.Enabled {
|
||||
// This target is already enabled using the
|
||||
// legacy environment variables, ignore.
|
||||
continue
|
||||
}
|
||||
enableEnv := EnvAuditWebhookEnable
|
||||
if target != config.Default {
|
||||
enableEnv = EnvAuditWebhookEnable + config.Default + target
|
||||
}
|
||||
enable, err := config.ParseBool(env.Get(enableEnv, ""))
|
||||
if err != nil || !enable {
|
||||
continue
|
||||
}
|
||||
endpointEnv := EnvAuditWebhookEndpoint
|
||||
if target != config.Default {
|
||||
endpointEnv = EnvAuditWebhookEndpoint + config.Default + target
|
||||
}
|
||||
authTokenEnv := EnvAuditWebhookAuthToken
|
||||
if target != config.Default {
|
||||
authTokenEnv = EnvAuditWebhookAuthToken + config.Default + target
|
||||
}
|
||||
clientCertEnv := EnvAuditWebhookClientCert
|
||||
if target != config.Default {
|
||||
clientCertEnv = EnvAuditWebhookClientCert + config.Default + target
|
||||
}
|
||||
clientKeyEnv := EnvAuditWebhookClientKey
|
||||
if target != config.Default {
|
||||
clientKeyEnv = EnvAuditWebhookClientKey + config.Default + target
|
||||
}
|
||||
err = config.EnsureCertAndKey(env.Get(clientCertEnv, ""), env.Get(clientKeyEnv, ""))
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
queueSizeEnv := EnvAuditWebhookQueueSize
|
||||
if target != config.Default {
|
||||
queueSizeEnv = EnvAuditWebhookQueueSize + config.Default + target
|
||||
}
|
||||
queueSize, err := strconv.Atoi(env.Get(queueSizeEnv, "100000"))
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
if queueSize <= 0 {
|
||||
return cfg, errors.New("invalid queue_size value")
|
||||
}
|
||||
cfg.AuditWebhook[target] = http.Config{
|
||||
Enabled: true,
|
||||
Endpoint: env.Get(endpointEnv, ""),
|
||||
AuthToken: env.Get(authTokenEnv, ""),
|
||||
ClientCert: env.Get(clientCertEnv, ""),
|
||||
ClientKey: env.Get(clientKeyEnv, ""),
|
||||
QueueSize: queueSize,
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// LookupConfigForSubSys - lookup logger config, override with ENVs if set, for the given sub-system
|
||||
func LookupConfigForSubSys(subSys string) (cfg Config, err error) {
|
||||
switch subSys {
|
||||
case config.LoggerWebhookSubSys:
|
||||
if cfg, err = lookupLoggerWebhookConfig(); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
case config.AuditWebhookSubSys:
|
||||
if cfg, err = lookupAuditWebhookConfig(); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// GetGlobalDeploymentID :
|
||||
func GetGlobalDeploymentID() string {
|
||||
if globalDeploymentID != "" {
|
||||
return globalDeploymentID
|
||||
}
|
||||
globalDeploymentID = env.Get(EnvGlobalDeploymentID, mustGetUUID())
|
||||
return globalDeploymentID
|
||||
}
|
||||
|
||||
// mustGetUUID - get a random UUID.
|
||||
func mustGetUUID() string {
|
||||
u, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
CriticalIf(GlobalContext, err)
|
||||
}
|
||||
return u.String()
|
||||
}
|
||||
80
pkg/logger/config/bool-flag.go
Normal file
80
pkg/logger/config/bool-flag.go
Normal file
@@ -0,0 +1,80 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BoolFlag - wrapper bool type.
|
||||
type BoolFlag bool
|
||||
|
||||
// String - returns string of BoolFlag.
|
||||
func (bf BoolFlag) String() string {
|
||||
if bf {
|
||||
return "on"
|
||||
}
|
||||
|
||||
return "off"
|
||||
}
|
||||
|
||||
// MarshalJSON - converts BoolFlag into JSON data.
|
||||
func (bf BoolFlag) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(bf.String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON - parses given data into BoolFlag.
|
||||
func (bf *BoolFlag) UnmarshalJSON(data []byte) (err error) {
|
||||
var s string
|
||||
if err = json.Unmarshal(data, &s); err == nil {
|
||||
b := BoolFlag(true)
|
||||
if s == "" {
|
||||
// Empty string is treated as valid.
|
||||
*bf = b
|
||||
} else if b, err = ParseBoolFlag(s); err == nil {
|
||||
*bf = b
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ParseBool returns the boolean value represented by the string.
|
||||
func ParseBool(str string) (bool, error) {
|
||||
switch str {
|
||||
case "1", "t", "T", "true", "TRUE", "True", "on", "ON", "On":
|
||||
return true, nil
|
||||
case "0", "f", "F", "false", "FALSE", "False", "off", "OFF", "Off":
|
||||
return false, nil
|
||||
}
|
||||
if strings.EqualFold(str, "enabled") {
|
||||
return true, nil
|
||||
}
|
||||
if strings.EqualFold(str, "disabled") {
|
||||
return false, nil
|
||||
}
|
||||
return false, fmt.Errorf("ParseBool: parsing '%s': %w", str, strconv.ErrSyntax)
|
||||
}
|
||||
|
||||
// ParseBoolFlag - parses string into BoolFlag.
|
||||
func ParseBoolFlag(s string) (bf BoolFlag, err error) {
|
||||
b, err := ParseBool(s)
|
||||
return BoolFlag(b), err
|
||||
}
|
||||
126
pkg/logger/config/bool-flag_test.go
Normal file
126
pkg/logger/config/bool-flag_test.go
Normal file
@@ -0,0 +1,126 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 config
|
||||
|
||||
import "testing"
|
||||
|
||||
// Test BoolFlag.String()
|
||||
func TestBoolFlagString(t *testing.T) {
|
||||
var bf BoolFlag
|
||||
|
||||
testCases := []struct {
|
||||
flag BoolFlag
|
||||
expectedResult string
|
||||
}{
|
||||
{bf, "off"},
|
||||
{BoolFlag(true), "on"},
|
||||
{BoolFlag(false), "off"},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
str := testCase.flag.String()
|
||||
if testCase.expectedResult != str {
|
||||
t.Fatalf("expected: %v, got: %v", testCase.expectedResult, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test BoolFlag.MarshalJSON()
|
||||
func TestBoolFlagMarshalJSON(t *testing.T) {
|
||||
var bf BoolFlag
|
||||
|
||||
testCases := []struct {
|
||||
flag BoolFlag
|
||||
expectedResult string
|
||||
}{
|
||||
{bf, `"off"`},
|
||||
{BoolFlag(true), `"on"`},
|
||||
{BoolFlag(false), `"off"`},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
data, _ := testCase.flag.MarshalJSON()
|
||||
if testCase.expectedResult != string(data) {
|
||||
t.Fatalf("expected: %v, got: %v", testCase.expectedResult, string(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test BoolFlag.UnmarshalJSON()
|
||||
func TestBoolFlagUnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedResult BoolFlag
|
||||
expectedErr bool
|
||||
}{
|
||||
{[]byte(`{}`), BoolFlag(false), true},
|
||||
{[]byte(`["on"]`), BoolFlag(false), true},
|
||||
{[]byte(`"junk"`), BoolFlag(false), true},
|
||||
{[]byte(`""`), BoolFlag(true), false},
|
||||
{[]byte(`"on"`), BoolFlag(true), false},
|
||||
{[]byte(`"off"`), BoolFlag(false), false},
|
||||
{[]byte(`"true"`), BoolFlag(true), false},
|
||||
{[]byte(`"false"`), BoolFlag(false), false},
|
||||
{[]byte(`"ON"`), BoolFlag(true), false},
|
||||
{[]byte(`"OFF"`), BoolFlag(false), false},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
var flag BoolFlag
|
||||
err := (&flag).UnmarshalJSON(testCase.data)
|
||||
if !testCase.expectedErr && err != nil {
|
||||
t.Fatalf("error: expected = <nil>, got = %v", err)
|
||||
}
|
||||
if testCase.expectedErr && err == nil {
|
||||
t.Fatalf("error: expected error, got = <nil>")
|
||||
}
|
||||
if err == nil && testCase.expectedResult != flag {
|
||||
t.Fatalf("result: expected: %v, got: %v", testCase.expectedResult, flag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test ParseBoolFlag()
|
||||
func TestParseBoolFlag(t *testing.T) {
|
||||
testCases := []struct {
|
||||
flagStr string
|
||||
expectedResult BoolFlag
|
||||
expectedErr bool
|
||||
}{
|
||||
{"", BoolFlag(false), true},
|
||||
{"junk", BoolFlag(false), true},
|
||||
{"true", BoolFlag(true), false},
|
||||
{"false", BoolFlag(false), false},
|
||||
{"ON", BoolFlag(true), false},
|
||||
{"OFF", BoolFlag(false), false},
|
||||
{"on", BoolFlag(true), false},
|
||||
{"off", BoolFlag(false), false},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
bf, err := ParseBoolFlag(testCase.flagStr)
|
||||
if !testCase.expectedErr && err != nil {
|
||||
t.Fatalf("error: expected = <nil>, got = %v", err)
|
||||
}
|
||||
if testCase.expectedErr && err == nil {
|
||||
t.Fatalf("error: expected error, got = <nil>")
|
||||
}
|
||||
if err == nil && testCase.expectedResult != bf {
|
||||
t.Fatalf("result: expected: %v, got: %v", testCase.expectedResult, bf)
|
||||
}
|
||||
}
|
||||
}
|
||||
30
pkg/logger/config/certs.go
Normal file
30
pkg/logger/config/certs.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// EnsureCertAndKey checks if both client certificate and key paths are provided
|
||||
func EnsureCertAndKey(clientCert, clientKey string) error {
|
||||
if (clientCert != "" && clientKey == "") ||
|
||||
(clientCert == "" && clientKey != "") {
|
||||
return errors.New("cert and key must be specified as a pair")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
34
pkg/logger/config/config.go
Normal file
34
pkg/logger/config/config.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 config
|
||||
|
||||
import (
|
||||
"github.com/minio/madmin-go"
|
||||
)
|
||||
|
||||
// Default keys
|
||||
const (
|
||||
Default = madmin.Default
|
||||
Enable = madmin.EnableKey
|
||||
License = "license" // Deprecated Dec 2021
|
||||
)
|
||||
|
||||
// Top level config constants.
|
||||
const (
|
||||
LoggerWebhookSubSys = "logger_webhook"
|
||||
AuditWebhookSubSys = "audit_webhook"
|
||||
)
|
||||
223
pkg/logger/console.go
Normal file
223
pkg/logger/console.go
Normal file
@@ -0,0 +1,223 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 logger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/logger/color"
|
||||
"github.com/minio/console/pkg/logger/message/log"
|
||||
c "github.com/minio/pkg/console"
|
||||
)
|
||||
|
||||
// ConsoleLoggerTgt is a stringified value to represent console logging
|
||||
const ConsoleLoggerTgt = "console+http"
|
||||
|
||||
// Logger interface describes the methods that need to be implemented to satisfy the interface requirements.
|
||||
type Logger interface {
|
||||
json(msg string, args ...interface{})
|
||||
quiet(msg string, args ...interface{})
|
||||
pretty(msg string, args ...interface{})
|
||||
}
|
||||
|
||||
func consoleLog(console Logger, msg string, args ...interface{}) {
|
||||
switch {
|
||||
case jsonFlag:
|
||||
// Strip escape control characters from json message
|
||||
msg = ansiRE.ReplaceAllLiteralString(msg, "")
|
||||
console.json(msg, args...)
|
||||
case quietFlag:
|
||||
console.quiet(msg+"\n", args...)
|
||||
default:
|
||||
console.pretty(msg+"\n", args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Fatal prints only fatal errors message with no stack trace
|
||||
// it will be called for input validation failures
|
||||
func Fatal(err error, msg string, data ...interface{}) {
|
||||
fatal(err, msg, data...)
|
||||
}
|
||||
|
||||
func fatal(err error, msg string, data ...interface{}) {
|
||||
var errMsg string
|
||||
if msg != "" {
|
||||
errMsg = errorFmtFunc(fmt.Sprintf(msg, data...), err, jsonFlag)
|
||||
} else {
|
||||
errMsg = err.Error()
|
||||
}
|
||||
consoleLog(fatalMessage, errMsg)
|
||||
}
|
||||
|
||||
var fatalMessage fatalMsg
|
||||
|
||||
type fatalMsg struct{}
|
||||
|
||||
func (f fatalMsg) json(msg string, args ...interface{}) {
|
||||
var message string
|
||||
if msg != "" {
|
||||
message = fmt.Sprintf(msg, args...)
|
||||
} else {
|
||||
message = fmt.Sprint(args...)
|
||||
}
|
||||
logJSON, err := json.Marshal(&log.Entry{
|
||||
Level: FatalLvl.String(),
|
||||
Message: message,
|
||||
Time: time.Now().UTC(),
|
||||
Trace: &log.Trace{Message: message, Source: []string{getSource(6)}},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(logJSON))
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (f fatalMsg) quiet(msg string, args ...interface{}) {
|
||||
f.pretty(msg, args...)
|
||||
}
|
||||
|
||||
var (
|
||||
logTag = "ERROR"
|
||||
logBanner = color.BgRed(color.FgWhite(color.Bold(logTag))) + " "
|
||||
emptyBanner = color.BgRed(strings.Repeat(" ", len(logTag))) + " "
|
||||
bannerWidth = len(logTag) + 1
|
||||
)
|
||||
|
||||
func (f fatalMsg) pretty(msg string, args ...interface{}) {
|
||||
// Build the passed errors message
|
||||
errMsg := fmt.Sprintf(msg, args...)
|
||||
|
||||
tagPrinted := false
|
||||
|
||||
// Print the errors message: the following code takes care
|
||||
// of splitting errors text and always pretty printing the
|
||||
// red banner along with the errors message. Since the errors
|
||||
// message itself contains some colored text, we needed
|
||||
// to use some ANSI control escapes to cursor color state
|
||||
// and freely move in the screen.
|
||||
for _, line := range strings.Split(errMsg, "\n") {
|
||||
if len(line) == 0 {
|
||||
// No more text to print, just quit.
|
||||
break
|
||||
}
|
||||
|
||||
for {
|
||||
// Save the attributes of the current cursor helps
|
||||
// us save the text color of the passed errors message
|
||||
ansiSaveAttributes()
|
||||
// Print banner with or without the log tag
|
||||
if !tagPrinted {
|
||||
c.Print(logBanner)
|
||||
tagPrinted = true
|
||||
} else {
|
||||
c.Print(emptyBanner)
|
||||
}
|
||||
// Restore the text color of the errors message
|
||||
ansiRestoreAttributes()
|
||||
ansiMoveRight(bannerWidth)
|
||||
// Continue errors message printing
|
||||
c.Println(line)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Exit because this is a fatal errors message
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
type infoMsg struct{}
|
||||
|
||||
var info infoMsg
|
||||
|
||||
func (i infoMsg) json(msg string, args ...interface{}) {
|
||||
var message string
|
||||
if msg != "" {
|
||||
message = fmt.Sprintf(msg, args...)
|
||||
} else {
|
||||
message = fmt.Sprint(args...)
|
||||
}
|
||||
logJSON, err := json.Marshal(&log.Entry{
|
||||
Level: InformationLvl.String(),
|
||||
Message: message,
|
||||
Time: time.Now().UTC(),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(logJSON))
|
||||
}
|
||||
|
||||
func (i infoMsg) quiet(msg string, args ...interface{}) {
|
||||
}
|
||||
|
||||
func (i infoMsg) pretty(msg string, args ...interface{}) {
|
||||
if msg == "" {
|
||||
c.Println(args...)
|
||||
}
|
||||
c.Printf(msg, args...)
|
||||
}
|
||||
|
||||
type errorMsg struct{}
|
||||
|
||||
var errorm errorMsg
|
||||
|
||||
func (i errorMsg) json(msg string, args ...interface{}) {
|
||||
var message string
|
||||
if msg != "" {
|
||||
message = fmt.Sprintf(msg, args...)
|
||||
} else {
|
||||
message = fmt.Sprint(args...)
|
||||
}
|
||||
logJSON, err := json.Marshal(&log.Entry{
|
||||
Level: ErrorLvl.String(),
|
||||
Message: message,
|
||||
Time: time.Now().UTC(),
|
||||
Trace: &log.Trace{Message: message, Source: []string{getSource(6)}},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(logJSON))
|
||||
}
|
||||
|
||||
func (i errorMsg) quiet(msg string, args ...interface{}) {
|
||||
i.pretty(msg, args...)
|
||||
}
|
||||
|
||||
func (i errorMsg) pretty(msg string, args ...interface{}) {
|
||||
if msg == "" {
|
||||
c.Println(args...)
|
||||
}
|
||||
c.Printf(msg, args...)
|
||||
c.Printf("\n")
|
||||
}
|
||||
|
||||
// Error :
|
||||
func Error(msg string, data ...interface{}) {
|
||||
consoleLog(errorm, msg, data...)
|
||||
}
|
||||
|
||||
// Info :
|
||||
func Info(msg string, data ...interface{}) {
|
||||
consoleLog(info, msg, data...)
|
||||
}
|
||||
56
pkg/logger/const.go
Normal file
56
pkg/logger/const.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/minio/console/pkg/logger/target/http"
|
||||
)
|
||||
|
||||
// Audit/Logger constants
|
||||
const (
|
||||
EnvLoggerJSONEnable = "CONSOLE_LOGGER_JSON_ENABLE"
|
||||
EnvLoggerAnonymousEnable = "CONSOLE_LOGGER_ANONYMOUS_ENABLE"
|
||||
EnvLoggerQuietEnable = "CONSOLE_LOGGER_QUIET_ENABLE"
|
||||
|
||||
EnvGlobalDeploymentID = "CONSOLE_GLOBAL_DEPLOYMENT_ID"
|
||||
EnvLoggerWebhookEnable = "CONSOLE_LOGGER_WEBHOOK_ENABLE"
|
||||
EnvLoggerWebhookEndpoint = "CONSOLE_LOGGER_WEBHOOK_ENDPOINT"
|
||||
EnvLoggerWebhookAuthToken = "CONSOLE_LOGGER_WEBHOOK_AUTH_TOKEN"
|
||||
EnvLoggerWebhookClientCert = "CONSOLE_LOGGER_WEBHOOK_CLIENT_CERT"
|
||||
EnvLoggerWebhookClientKey = "CONSOLE_LOGGER_WEBHOOK_CLIENT_KEY"
|
||||
EnvLoggerWebhookQueueSize = "CONSOLE_LOGGER_WEBHOOK_QUEUE_SIZE"
|
||||
|
||||
EnvAuditWebhookEnable = "CONSOLE_AUDIT_WEBHOOK_ENABLE"
|
||||
EnvAuditWebhookEndpoint = "CONSOLE_AUDIT_WEBHOOK_ENDPOINT"
|
||||
EnvAuditWebhookAuthToken = "CONSOLE_AUDIT_WEBHOOK_AUTH_TOKEN"
|
||||
EnvAuditWebhookClientCert = "CONSOLE_AUDIT_WEBHOOK_CLIENT_CERT"
|
||||
EnvAuditWebhookClientKey = "CONSOLE_AUDIT_WEBHOOK_CLIENT_KEY"
|
||||
EnvAuditWebhookQueueSize = "CONSOLE_AUDIT_WEBHOOK_QUEUE_SIZE"
|
||||
)
|
||||
|
||||
// Config console and http logger targets
|
||||
type Config struct {
|
||||
HTTP map[string]http.Config `json:"http"`
|
||||
AuditWebhook map[string]http.Config `json:"audit"`
|
||||
}
|
||||
|
||||
var (
|
||||
globalDeploymentID string
|
||||
GlobalContext context.Context
|
||||
)
|
||||
480
pkg/logger/logger.go
Normal file
480
pkg/logger/logger.go
Normal file
@@ -0,0 +1,480 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/minio/pkg/env"
|
||||
|
||||
"github.com/minio/console/pkg"
|
||||
"github.com/minio/pkg/certs"
|
||||
|
||||
"github.com/minio/console/pkg/logger/config"
|
||||
"github.com/minio/console/pkg/logger/message/log"
|
||||
"github.com/minio/highwayhash"
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
)
|
||||
|
||||
// HighwayHash key for logging in anonymous mode
|
||||
var magicHighwayHash256Key = []byte("\x4b\xe7\x34\xfa\x8e\x23\x8a\xcd\x26\x3e\x83\xe6\xbb\x96\x85\x52\x04\x0f\x93\x5d\xa3\x9f\x44\x14\x97\xe0\x9d\x13\x22\xde\x36\xa0")
|
||||
|
||||
// Disable disables all logging, false by default. (used for "go test")
|
||||
var Disable = false
|
||||
|
||||
// Level type
|
||||
type Level int8
|
||||
|
||||
// Enumerated level types
|
||||
const (
|
||||
InformationLvl Level = iota + 1
|
||||
ErrorLvl
|
||||
FatalLvl
|
||||
)
|
||||
|
||||
var trimStrings []string
|
||||
|
||||
// TimeFormat - logging time format.
|
||||
const TimeFormat string = "15:04:05 MST 01/02/2006"
|
||||
|
||||
var matchingFuncNames = [...]string{
|
||||
"http.HandlerFunc.ServeHTTP",
|
||||
"cmd.serverMain",
|
||||
"cmd.StartGateway",
|
||||
// add more here ..
|
||||
}
|
||||
|
||||
func (level Level) String() string {
|
||||
var lvlStr string
|
||||
switch level {
|
||||
case InformationLvl:
|
||||
lvlStr = "INFO"
|
||||
case ErrorLvl:
|
||||
lvlStr = "ERROR"
|
||||
case FatalLvl:
|
||||
lvlStr = "FATAL"
|
||||
}
|
||||
return lvlStr
|
||||
}
|
||||
|
||||
// quietFlag: Hide startup messages if enabled
|
||||
// jsonFlag: Display in JSON format, if enabled
|
||||
var (
|
||||
quietFlag, jsonFlag, anonFlag bool
|
||||
// Custom function to format errors
|
||||
errorFmtFunc func(string, error, bool) string
|
||||
)
|
||||
|
||||
// EnableQuiet - turns quiet option on.
|
||||
func EnableQuiet() {
|
||||
quietFlag = true
|
||||
}
|
||||
|
||||
// EnableJSON - outputs logs in json format.
|
||||
func EnableJSON() {
|
||||
jsonFlag = true
|
||||
quietFlag = true
|
||||
}
|
||||
|
||||
// EnableAnonymous - turns anonymous flag
|
||||
// to avoid printing sensitive information.
|
||||
func EnableAnonymous() {
|
||||
anonFlag = true
|
||||
}
|
||||
|
||||
// IsAnonymous - returns true if anonFlag is true
|
||||
func IsAnonymous() bool {
|
||||
return anonFlag
|
||||
}
|
||||
|
||||
// IsJSON - returns true if jsonFlag is true
|
||||
func IsJSON() bool {
|
||||
return jsonFlag
|
||||
}
|
||||
|
||||
// IsQuiet - returns true if quietFlag is true
|
||||
func IsQuiet() bool {
|
||||
return quietFlag
|
||||
}
|
||||
|
||||
// RegisterError registers the specified rendering function. This latter
|
||||
// will be called for a pretty rendering of fatal errors.
|
||||
func RegisterError(f func(string, error, bool) string) {
|
||||
errorFmtFunc = f
|
||||
}
|
||||
|
||||
// Remove any duplicates and return unique entries.
|
||||
func uniqueEntries(paths []string) []string {
|
||||
m := make(set.StringSet)
|
||||
for _, p := range paths {
|
||||
if !m.Contains(p) {
|
||||
m.Add(p)
|
||||
}
|
||||
}
|
||||
return m.ToSlice()
|
||||
}
|
||||
|
||||
// Init sets the trimStrings to possible GOPATHs
|
||||
// and GOROOT directories. Also append github.com/minio/minio
|
||||
// This is done to clean up the filename, when stack trace is
|
||||
// displayed when an errors happens.
|
||||
func Init(goPath string, goRoot string) {
|
||||
var goPathList []string
|
||||
var goRootList []string
|
||||
var defaultgoPathList []string
|
||||
var defaultgoRootList []string
|
||||
pathSeperator := ":"
|
||||
// Add all possible GOPATH paths into trimStrings
|
||||
// Split GOPATH depending on the OS type
|
||||
if runtime.GOOS == "windows" {
|
||||
pathSeperator = ";"
|
||||
}
|
||||
|
||||
goPathList = strings.Split(goPath, pathSeperator)
|
||||
goRootList = strings.Split(goRoot, pathSeperator)
|
||||
defaultgoPathList = strings.Split(build.Default.GOPATH, pathSeperator)
|
||||
defaultgoRootList = strings.Split(build.Default.GOROOT, pathSeperator)
|
||||
|
||||
// Add trim string "{GOROOT}/src/" into trimStrings
|
||||
trimStrings = []string{filepath.Join(runtime.GOROOT(), "src") + string(filepath.Separator)}
|
||||
|
||||
// Add all possible path from GOPATH=path1:path2...:pathN
|
||||
// as "{path#}/src/" into trimStrings
|
||||
for _, goPathString := range goPathList {
|
||||
trimStrings = append(trimStrings, filepath.Join(goPathString, "src")+string(filepath.Separator))
|
||||
}
|
||||
|
||||
for _, goRootString := range goRootList {
|
||||
trimStrings = append(trimStrings, filepath.Join(goRootString, "src")+string(filepath.Separator))
|
||||
}
|
||||
|
||||
for _, defaultgoPathString := range defaultgoPathList {
|
||||
trimStrings = append(trimStrings, filepath.Join(defaultgoPathString, "src")+string(filepath.Separator))
|
||||
}
|
||||
|
||||
for _, defaultgoRootString := range defaultgoRootList {
|
||||
trimStrings = append(trimStrings, filepath.Join(defaultgoRootString, "src")+string(filepath.Separator))
|
||||
}
|
||||
|
||||
// Remove duplicate entries.
|
||||
trimStrings = uniqueEntries(trimStrings)
|
||||
|
||||
// Add "github.com/minio/minio" as the last to cover
|
||||
// paths like "{GOROOT}/src/github.com/minio/minio"
|
||||
// and "{GOPATH}/src/github.com/minio/minio"
|
||||
trimStrings = append(trimStrings, filepath.Join("github.com", "minio", "minio")+string(filepath.Separator))
|
||||
}
|
||||
|
||||
func trimTrace(f string) string {
|
||||
for _, trimString := range trimStrings {
|
||||
f = strings.TrimPrefix(filepath.ToSlash(f), filepath.ToSlash(trimString))
|
||||
}
|
||||
return filepath.FromSlash(f)
|
||||
}
|
||||
|
||||
func getSource(level int) string {
|
||||
pc, file, lineNumber, ok := runtime.Caller(level)
|
||||
if ok {
|
||||
// Clean up the common prefixes
|
||||
file = trimTrace(file)
|
||||
_, funcName := filepath.Split(runtime.FuncForPC(pc).Name())
|
||||
return fmt.Sprintf("%v:%v:%v()", file, lineNumber, funcName)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getTrace method - creates and returns stack trace
|
||||
func getTrace(traceLevel int) []string {
|
||||
var trace []string
|
||||
pc, file, lineNumber, ok := runtime.Caller(traceLevel)
|
||||
|
||||
for ok && file != "" {
|
||||
// Clean up the common prefixes
|
||||
file = trimTrace(file)
|
||||
// Get the function name
|
||||
_, funcName := filepath.Split(runtime.FuncForPC(pc).Name())
|
||||
// Skip duplicate traces that start with file name, "<autogenerated>"
|
||||
// and also skip traces with function name that starts with "runtime."
|
||||
if !strings.HasPrefix(file, "<autogenerated>") &&
|
||||
!strings.HasPrefix(funcName, "runtime.") {
|
||||
// Form and append a line of stack trace into a
|
||||
// collection, 'trace', to build full stack trace
|
||||
trace = append(trace, fmt.Sprintf("%v:%v:%v()", file, lineNumber, funcName))
|
||||
|
||||
// Ignore trace logs beyond the following conditions
|
||||
for _, name := range matchingFuncNames {
|
||||
if funcName == name {
|
||||
return trace
|
||||
}
|
||||
}
|
||||
}
|
||||
traceLevel++
|
||||
// Read stack trace information from PC
|
||||
pc, file, lineNumber, ok = runtime.Caller(traceLevel)
|
||||
}
|
||||
return trace
|
||||
}
|
||||
|
||||
// Return the highway hash of the passed string
|
||||
func hashString(input string) string {
|
||||
hh, _ := highwayhash.New(magicHighwayHash256Key)
|
||||
hh.Write([]byte(input))
|
||||
return hex.EncodeToString(hh.Sum(nil))
|
||||
}
|
||||
|
||||
// Kind specifies the kind of errors log
|
||||
type Kind string
|
||||
|
||||
const (
|
||||
// Minio errors
|
||||
Minio Kind = "CONSOLE"
|
||||
// Application errors
|
||||
Application Kind = "APPLICATION"
|
||||
// All errors
|
||||
All Kind = "ALL"
|
||||
)
|
||||
|
||||
// LogAlwaysIf prints a detailed errors message during
|
||||
// the execution of the server.
|
||||
func LogAlwaysIf(ctx context.Context, err error, errKind ...interface{}) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
logIf(ctx, err, errKind...)
|
||||
}
|
||||
|
||||
// LogIf prints a detailed errors message during
|
||||
// the execution of the server
|
||||
func LogIf(ctx context.Context, err error, errKind ...interface{}) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return
|
||||
}
|
||||
logIf(ctx, err, errKind...)
|
||||
}
|
||||
|
||||
// logIf prints a detailed errors message during
|
||||
// the execution of the server.
|
||||
func logIf(ctx context.Context, err error, errKind ...interface{}) {
|
||||
if Disable {
|
||||
return
|
||||
}
|
||||
logKind := string(Minio)
|
||||
if len(errKind) > 0 {
|
||||
if ek, ok := errKind[0].(Kind); ok {
|
||||
logKind = string(ek)
|
||||
}
|
||||
}
|
||||
req := GetReqInfo(ctx)
|
||||
|
||||
if req == nil {
|
||||
req = &ReqInfo{API: "SYSTEM"}
|
||||
}
|
||||
|
||||
kv := req.GetTags()
|
||||
tags := make(map[string]interface{}, len(kv))
|
||||
for _, entry := range kv {
|
||||
tags[entry.Key] = entry.Val
|
||||
}
|
||||
|
||||
// Get full stack trace
|
||||
trace := getTrace(3)
|
||||
|
||||
// Get the cause for the Error
|
||||
message := fmt.Sprintf("%v (%T)", err, err)
|
||||
if req.DeploymentID == "" {
|
||||
req.DeploymentID = GetGlobalDeploymentID()
|
||||
}
|
||||
|
||||
entry := log.Entry{
|
||||
DeploymentID: req.DeploymentID,
|
||||
Level: ErrorLvl.String(),
|
||||
LogKind: logKind,
|
||||
RemoteHost: req.RemoteHost,
|
||||
Host: req.Host,
|
||||
RequestID: req.RequestID,
|
||||
SessionID: req.SessionID,
|
||||
UserAgent: req.UserAgent,
|
||||
Time: time.Now().UTC(),
|
||||
Trace: &log.Trace{
|
||||
Message: message,
|
||||
Source: trace,
|
||||
Variables: tags,
|
||||
},
|
||||
}
|
||||
|
||||
if anonFlag {
|
||||
entry.SessionID = hashString(entry.SessionID)
|
||||
entry.RemoteHost = hashString(entry.RemoteHost)
|
||||
entry.Trace.Message = reflect.TypeOf(err).String()
|
||||
entry.Trace.Variables = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// Iterate over all logger targets to send the log entry
|
||||
for _, t := range SystemTargets() {
|
||||
if err := t.Send(entry, entry.LogKind); err != nil {
|
||||
if consoleTgt != nil {
|
||||
entry.Trace.Message = fmt.Sprintf("event(%#v) was not sent to Logger target (%#v): %#v", entry, t, err)
|
||||
consoleTgt.Send(entry, entry.LogKind)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ErrCritical is the value panic'd whenever CriticalIf is called.
|
||||
var ErrCritical struct{}
|
||||
|
||||
// CriticalIf logs the provided errors on the console. It fails the
|
||||
// current go-routine by causing a `panic(ErrCritical)`.
|
||||
func CriticalIf(ctx context.Context, err error, errKind ...interface{}) {
|
||||
if err != nil {
|
||||
LogIf(ctx, err, errKind...)
|
||||
panic(ErrCritical)
|
||||
}
|
||||
}
|
||||
|
||||
// FatalIf is similar to Fatal() but it ignores passed nil errors
|
||||
func FatalIf(err error, msg string, data ...interface{}) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
fatal(err, msg, data...)
|
||||
}
|
||||
|
||||
func applyDynamicConfigForSubSys(ctx context.Context, transport *http.Transport, subSys string) error {
|
||||
switch subSys {
|
||||
case config.LoggerWebhookSubSys:
|
||||
loggerCfg, err := LookupConfigForSubSys(config.LoggerWebhookSubSys)
|
||||
if err != nil {
|
||||
LogIf(ctx, fmt.Errorf("unable to load logger webhook config: %w", err))
|
||||
return err
|
||||
}
|
||||
userAgent := getUserAgent()
|
||||
for n, l := range loggerCfg.HTTP {
|
||||
if l.Enabled {
|
||||
l.LogOnce = LogOnceIf
|
||||
l.UserAgent = userAgent
|
||||
l.Transport = NewHTTPTransportWithClientCerts(transport, l.ClientCert, l.ClientKey)
|
||||
loggerCfg.HTTP[n] = l
|
||||
}
|
||||
}
|
||||
err = UpdateSystemTargets(loggerCfg)
|
||||
if err != nil {
|
||||
LogIf(ctx, fmt.Errorf("unable to update logger webhook config: %w", err))
|
||||
return err
|
||||
}
|
||||
case config.AuditWebhookSubSys:
|
||||
loggerCfg, err := LookupConfigForSubSys(config.AuditWebhookSubSys)
|
||||
if err != nil {
|
||||
LogIf(ctx, fmt.Errorf("unable to load audit webhook config: %w", err))
|
||||
return err
|
||||
}
|
||||
userAgent := getUserAgent()
|
||||
for n, l := range loggerCfg.AuditWebhook {
|
||||
if l.Enabled {
|
||||
l.LogOnce = LogOnceIf
|
||||
l.UserAgent = userAgent
|
||||
l.Transport = NewHTTPTransportWithClientCerts(transport, l.ClientCert, l.ClientKey)
|
||||
loggerCfg.AuditWebhook[n] = l
|
||||
}
|
||||
}
|
||||
|
||||
err = UpdateAuditWebhookTargets(loggerCfg)
|
||||
if err != nil {
|
||||
LogIf(ctx, fmt.Errorf("Unable to update audit webhook targets: %w", err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitializeLogger :
|
||||
func InitializeLogger(ctx context.Context, transport *http.Transport) error {
|
||||
err := applyDynamicConfigForSubSys(ctx, transport, config.LoggerWebhookSubSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = applyDynamicConfigForSubSys(ctx, transport, config.AuditWebhookSubSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if enable, _ := config.ParseBool(env.Get(EnvLoggerJSONEnable, "")); enable {
|
||||
EnableJSON()
|
||||
}
|
||||
if enable, _ := config.ParseBool(env.Get(EnvLoggerAnonymousEnable, "")); enable {
|
||||
EnableAnonymous()
|
||||
}
|
||||
if enable, _ := config.ParseBool(env.Get(EnvLoggerQuietEnable, "")); enable {
|
||||
EnableQuiet()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUserAgent() string {
|
||||
userAgentParts := []string{}
|
||||
// Helper function to concisely append a pair of strings to a
|
||||
// the user-agent slice.
|
||||
uaAppend := func(p, q string) {
|
||||
userAgentParts = append(userAgentParts, p, q)
|
||||
}
|
||||
uaAppend("Console (", runtime.GOOS)
|
||||
uaAppend("; ", runtime.GOARCH)
|
||||
uaAppend(") Console/", pkg.Version)
|
||||
uaAppend(" Console/", pkg.ReleaseTag)
|
||||
uaAppend(" Console/", pkg.CommitID)
|
||||
|
||||
return strings.Join(userAgentParts, "")
|
||||
}
|
||||
|
||||
// NewHTTPTransportWithClientCerts returns a new http configuration
|
||||
// used while communicating with the cloud backends.
|
||||
func NewHTTPTransportWithClientCerts(parentTransport *http.Transport, clientCert, clientKey string) *http.Transport {
|
||||
transport := parentTransport.Clone()
|
||||
if clientCert != "" && clientKey != "" {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
c, err := certs.NewManager(ctx, clientCert, clientKey, tls.LoadX509KeyPair)
|
||||
if err != nil {
|
||||
LogIf(ctx, fmt.Errorf("failed to load client key and cert, please check your endpoint configuration: %s",
|
||||
err.Error()))
|
||||
}
|
||||
if c != nil {
|
||||
c.UpdateReloadDuration(10 * time.Second)
|
||||
c.ReloadOnSignal(syscall.SIGHUP) // allow reloads upon SIGHUP
|
||||
transport.TLSClientConfig.GetClientCertificate = c.GetClientCertificate
|
||||
}
|
||||
}
|
||||
return transport
|
||||
}
|
||||
243
pkg/logger/logger_test.go
Normal file
243
pkg/logger/logger_test.go
Normal file
@@ -0,0 +1,243 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testServer(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func TestInitializeLogger(t *testing.T) {
|
||||
testServerWillStart := make(chan interface{})
|
||||
http.HandleFunc("/", testServer)
|
||||
go func() {
|
||||
close(testServerWillStart)
|
||||
err := http.ListenAndServe("127.0.0.1:1337", nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
<-testServerWillStart
|
||||
|
||||
loggerWebhookEnable := fmt.Sprintf("%s_TEST", EnvLoggerWebhookEnable)
|
||||
loggerWebhookEndpoint := fmt.Sprintf("%s_TEST", EnvLoggerWebhookEndpoint)
|
||||
loggerWebhookAuthToken := fmt.Sprintf("%s_TEST", EnvLoggerWebhookAuthToken)
|
||||
loggerWebhookClientCert := fmt.Sprintf("%s_TEST", EnvLoggerWebhookClientCert)
|
||||
loggerWebhookClientKey := fmt.Sprintf("%s_TEST", EnvLoggerWebhookClientKey)
|
||||
loggerWebhookQueueSize := fmt.Sprintf("%s_TEST", EnvLoggerWebhookQueueSize)
|
||||
|
||||
auditWebhookEnable := fmt.Sprintf("%s_TEST", EnvAuditWebhookEnable)
|
||||
auditWebhookEndpoint := fmt.Sprintf("%s_TEST", EnvAuditWebhookEndpoint)
|
||||
auditWebhookAuthToken := fmt.Sprintf("%s_TEST", EnvAuditWebhookAuthToken)
|
||||
auditWebhookClientCert := fmt.Sprintf("%s_TEST", EnvAuditWebhookClientCert)
|
||||
auditWebhookClientKey := fmt.Sprintf("%s_TEST", EnvAuditWebhookClientKey)
|
||||
auditWebhookQueueSize := fmt.Sprintf("%s_TEST", EnvAuditWebhookQueueSize)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
transport *http.Transport
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
setEnvVars func()
|
||||
unsetEnvVars func()
|
||||
}{
|
||||
{
|
||||
name: "logger or auditlog is not enabled",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
transport: http.DefaultTransport.(*http.Transport).Clone(),
|
||||
},
|
||||
wantErr: false,
|
||||
setEnvVars: func() {
|
||||
},
|
||||
unsetEnvVars: func() {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "logger webhook initialized correctly",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
transport: http.DefaultTransport.(*http.Transport).Clone(),
|
||||
},
|
||||
wantErr: false,
|
||||
setEnvVars: func() {
|
||||
os.Setenv(loggerWebhookEnable, "on")
|
||||
os.Setenv(loggerWebhookEndpoint, "http://127.0.0.1:1337/logger")
|
||||
os.Setenv(loggerWebhookAuthToken, "test")
|
||||
os.Setenv(loggerWebhookClientCert, "")
|
||||
os.Setenv(loggerWebhookClientKey, "")
|
||||
os.Setenv(loggerWebhookQueueSize, "1000")
|
||||
},
|
||||
unsetEnvVars: func() {
|
||||
os.Unsetenv(loggerWebhookEnable)
|
||||
os.Unsetenv(loggerWebhookEndpoint)
|
||||
os.Unsetenv(loggerWebhookAuthToken)
|
||||
os.Unsetenv(loggerWebhookClientCert)
|
||||
os.Unsetenv(loggerWebhookClientKey)
|
||||
os.Unsetenv(loggerWebhookQueueSize)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "logger webhook failed to initialize",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
transport: http.DefaultTransport.(*http.Transport).Clone(),
|
||||
},
|
||||
wantErr: true,
|
||||
setEnvVars: func() {
|
||||
os.Setenv(loggerWebhookEnable, "on")
|
||||
os.Setenv(loggerWebhookEndpoint, "https://aklsjdakljdjkalsd.com")
|
||||
os.Setenv(loggerWebhookAuthToken, "test")
|
||||
os.Setenv(loggerWebhookClientCert, "")
|
||||
os.Setenv(loggerWebhookClientKey, "")
|
||||
os.Setenv(loggerWebhookQueueSize, "1000")
|
||||
},
|
||||
unsetEnvVars: func() {
|
||||
os.Unsetenv(loggerWebhookEnable)
|
||||
os.Unsetenv(loggerWebhookEndpoint)
|
||||
os.Unsetenv(loggerWebhookAuthToken)
|
||||
os.Unsetenv(loggerWebhookClientCert)
|
||||
os.Unsetenv(loggerWebhookClientKey)
|
||||
os.Unsetenv(loggerWebhookQueueSize)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "auditlog webhook initialized correctly",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
transport: http.DefaultTransport.(*http.Transport).Clone(),
|
||||
},
|
||||
wantErr: false,
|
||||
setEnvVars: func() {
|
||||
os.Setenv(auditWebhookEnable, "on")
|
||||
os.Setenv(auditWebhookEndpoint, "http://127.0.0.1:1337/audit")
|
||||
os.Setenv(auditWebhookAuthToken, "test")
|
||||
os.Setenv(auditWebhookClientCert, "")
|
||||
os.Setenv(auditWebhookClientKey, "")
|
||||
os.Setenv(auditWebhookQueueSize, "1000")
|
||||
},
|
||||
unsetEnvVars: func() {
|
||||
os.Unsetenv(auditWebhookEnable)
|
||||
os.Unsetenv(auditWebhookEndpoint)
|
||||
os.Unsetenv(auditWebhookAuthToken)
|
||||
os.Unsetenv(auditWebhookClientCert)
|
||||
os.Unsetenv(auditWebhookClientKey)
|
||||
os.Unsetenv(auditWebhookQueueSize)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "auditlog webhook failed to initialize",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
transport: http.DefaultTransport.(*http.Transport).Clone(),
|
||||
},
|
||||
wantErr: true,
|
||||
setEnvVars: func() {
|
||||
os.Setenv(auditWebhookEnable, "on")
|
||||
os.Setenv(auditWebhookEndpoint, "https://aklsjdakljdjkalsd.com")
|
||||
os.Setenv(auditWebhookAuthToken, "test")
|
||||
os.Setenv(auditWebhookClientCert, "")
|
||||
os.Setenv(auditWebhookClientKey, "")
|
||||
os.Setenv(auditWebhookQueueSize, "1000")
|
||||
},
|
||||
unsetEnvVars: func() {
|
||||
os.Unsetenv(auditWebhookEnable)
|
||||
os.Unsetenv(auditWebhookEndpoint)
|
||||
os.Unsetenv(auditWebhookAuthToken)
|
||||
os.Unsetenv(auditWebhookClientCert)
|
||||
os.Unsetenv(auditWebhookClientKey)
|
||||
os.Unsetenv(auditWebhookQueueSize)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.setEnvVars != nil {
|
||||
tt.setEnvVars()
|
||||
}
|
||||
if err := InitializeLogger(tt.args.ctx, tt.args.transport); (err != nil) != tt.wantErr {
|
||||
t.Errorf("InitializeLogger() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if tt.unsetEnvVars != nil {
|
||||
tt.unsetEnvVars()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
}{
|
||||
{
|
||||
name: "enable json",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
EnableJSON()
|
||||
if !IsJSON() {
|
||||
t.Errorf("EnableJSON() = %v, want %v", IsJSON(), true)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableQuiet(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
}{
|
||||
{
|
||||
name: "enable quiet",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
EnableQuiet()
|
||||
if !IsQuiet() {
|
||||
t.Errorf("EnableQuiet() = %v, want %v", IsQuiet(), true)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableAnonymous(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
}{
|
||||
{
|
||||
name: "enable anonymous",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
EnableAnonymous()
|
||||
if !IsAnonymous() {
|
||||
t.Errorf("EnableAnonymous() = %v, want %v", IsAnonymous(), true)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
92
pkg/logger/logonce.go
Normal file
92
pkg/logger/logonce.go
Normal file
@@ -0,0 +1,92 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Holds a map of recently logged errors.
|
||||
type logOnceType struct {
|
||||
IDMap map[interface{}]error
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// One log message per errors.
|
||||
func (l *logOnceType) logOnceIf(ctx context.Context, err error, id interface{}, errKind ...interface{}) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
l.Lock()
|
||||
shouldLog := false
|
||||
prevErr := l.IDMap[id]
|
||||
if prevErr == nil {
|
||||
l.IDMap[id] = err
|
||||
shouldLog = true
|
||||
} else if prevErr.Error() != err.Error() {
|
||||
l.IDMap[id] = err
|
||||
shouldLog = true
|
||||
}
|
||||
l.Unlock()
|
||||
|
||||
if shouldLog {
|
||||
LogIf(ctx, err, errKind...)
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup the map every 30 minutes so that the log message is printed again for the user to notice.
|
||||
func (l *logOnceType) cleanupRoutine() {
|
||||
for {
|
||||
l.Lock()
|
||||
l.IDMap = make(map[interface{}]error)
|
||||
l.Unlock()
|
||||
|
||||
time.Sleep(30 * time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns logOnceType
|
||||
func newLogOnceType() *logOnceType {
|
||||
l := &logOnceType{IDMap: make(map[interface{}]error)}
|
||||
go l.cleanupRoutine()
|
||||
return l
|
||||
}
|
||||
|
||||
var logOnce = newLogOnceType()
|
||||
|
||||
// LogOnceIf - Logs notification errors - once per errors.
|
||||
// id is a unique identifier for related log messages, refer to cmd/notification.go
|
||||
// on how it is used.
|
||||
func LogOnceIf(ctx context.Context, err error, id interface{}, errKind ...interface{}) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return
|
||||
}
|
||||
|
||||
if err.Error() == http.ErrServerClosed.Error() || err.Error() == "disk not found" {
|
||||
return
|
||||
}
|
||||
|
||||
logOnce.logOnceIf(ctx, err, id, errKind...)
|
||||
}
|
||||
130
pkg/logger/message/audit/entry.go
Normal file
130
pkg/logger/message/audit/entry.go
Normal file
@@ -0,0 +1,130 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 audit
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
)
|
||||
|
||||
// Version - represents the current version of audit log structure.
|
||||
const Version = "1"
|
||||
|
||||
// ObjectVersion object version key/versionId
|
||||
type ObjectVersion struct {
|
||||
ObjectName string `json:"objectName"`
|
||||
VersionID string `json:"versionId,omitempty"`
|
||||
}
|
||||
|
||||
// Entry - audit entry logs.
|
||||
type Entry struct {
|
||||
Version string `json:"version"`
|
||||
DeploymentID string `json:"deploymentid,omitempty"`
|
||||
Time time.Time `json:"time"`
|
||||
Trigger string `json:"trigger"`
|
||||
API struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Method string `json:"method"`
|
||||
StatusCode int `json:"statusCode,omitempty"`
|
||||
InputBytes int64 `json:"rx"`
|
||||
OutputBytes int64 `json:"tx"`
|
||||
TimeToFirstByte string `json:"timeToFirstByte,omitempty"`
|
||||
TimeToResponse string `json:"timeToResponse,omitempty"`
|
||||
} `json:"api"`
|
||||
RemoteHost string `json:"remotehost,omitempty"`
|
||||
RequestID string `json:"requestID,omitempty"`
|
||||
SessionID string `json:"sessionID,omitempty"`
|
||||
UserAgent string `json:"userAgent,omitempty"`
|
||||
ReqClaims map[string]interface{} `json:"requestClaims,omitempty"`
|
||||
ReqQuery map[string]string `json:"requestQuery,omitempty"`
|
||||
ReqHeader map[string]string `json:"requestHeader,omitempty"`
|
||||
RespHeader map[string]string `json:"responseHeader,omitempty"`
|
||||
Tags map[string]interface{} `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// NewEntry - constructs an audit entry object with some fields filled
|
||||
func NewEntry(deploymentID string) Entry {
|
||||
return Entry{
|
||||
Version: Version,
|
||||
DeploymentID: deploymentID,
|
||||
Time: time.Now().UTC(),
|
||||
}
|
||||
}
|
||||
|
||||
// ToEntry - constructs an audit entry from a http request
|
||||
func ToEntry(w http.ResponseWriter, r *http.Request, reqClaims map[string]interface{}, deploymentID string) Entry {
|
||||
entry := NewEntry(deploymentID)
|
||||
|
||||
entry.RemoteHost = r.RemoteAddr
|
||||
entry.UserAgent = r.UserAgent()
|
||||
entry.ReqClaims = reqClaims
|
||||
|
||||
q := r.URL.Query()
|
||||
reqQuery := make(map[string]string, len(q))
|
||||
for k, v := range q {
|
||||
reqQuery[k] = strings.Join(v, ",")
|
||||
}
|
||||
entry.ReqQuery = reqQuery
|
||||
|
||||
reqHeader := make(map[string]string, len(r.Header))
|
||||
for k, v := range r.Header {
|
||||
reqHeader[k] = strings.Join(v, ",")
|
||||
}
|
||||
entry.ReqHeader = reqHeader
|
||||
|
||||
wh := w.Header()
|
||||
|
||||
var requestID interface{}
|
||||
requestID = r.Context().Value(utils.ContextRequestID)
|
||||
if requestID == nil {
|
||||
requestID, _ = utils.NewUUID()
|
||||
}
|
||||
entry.RequestID = requestID.(string)
|
||||
|
||||
if val := r.Context().Value(utils.ContextRequestUserID); val != nil {
|
||||
sessionID := val.(string)
|
||||
if os.Getenv("CONSOLE_OPERATOR_MODE") != "" && os.Getenv("CONSOLE_OPERATOR_MODE") == "on" {
|
||||
claims := jwt.MapClaims{}
|
||||
_, _ = jwt.ParseWithClaims(sessionID, claims, nil)
|
||||
if sub, ok := claims["sub"]; ok {
|
||||
sessionID = sub.(string)
|
||||
}
|
||||
}
|
||||
entry.SessionID = sessionID
|
||||
}
|
||||
|
||||
respHeader := make(map[string]string, len(wh))
|
||||
for k, v := range wh {
|
||||
respHeader[k] = strings.Join(v, ",")
|
||||
}
|
||||
entry.RespHeader = respHeader
|
||||
|
||||
if etag := respHeader[xhttp.ETag]; etag != "" {
|
||||
respHeader[xhttp.ETag] = strings.Trim(etag, `"`)
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
126
pkg/logger/message/audit/entry_test.go
Normal file
126
pkg/logger/message/audit/entry_test.go
Normal file
@@ -0,0 +1,126 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 audit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
)
|
||||
|
||||
func TestNewEntry(t *testing.T) {
|
||||
type args struct {
|
||||
deploymentID string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Entry
|
||||
}{
|
||||
{
|
||||
name: "constructs an audit entry object with some fields filled",
|
||||
args: args{
|
||||
deploymentID: "1",
|
||||
},
|
||||
want: Entry{
|
||||
Version: Version,
|
||||
DeploymentID: "1",
|
||||
Time: time.Now().UTC(),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := NewEntry(tt.args.deploymentID); got.DeploymentID != tt.want.DeploymentID {
|
||||
t.Errorf("NewEntry() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToEntry(t *testing.T) {
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/tenants?test=xyz", nil)
|
||||
req.Header.Set("Authorization", "xyz")
|
||||
req.Header.Set("ETag", "\"ABCDE\"")
|
||||
|
||||
// applying context information
|
||||
ctx := context.WithValue(req.Context(), utils.ContextRequestUserID, "eyJhbGciOiJSUzI1NiIsImtpZCI6Ing5cS0wSkEwQzFMWDJlRlR3dHo2b0t0NVNnRzJad0llMGVNczMxbjU0b2sifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJtaW5pby1vcGVyYXRvciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJjb25zb2xlLXNhLXRva2VuLWJrZzZwIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImNvbnNvbGUtc2EiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJhZTE2ZGVkNS01MmM3LTRkZTQtOWUxYS1iNmI4NGU2OGMzM2UiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6bWluaW8tb3BlcmF0b3I6Y29uc29sZS1zYSJ9.AjhzekAPC59SQVBQL5sr-1dqr57-jH8a5LVazpnEr_cC0JqT4jXYjdfbrZSF9yaL4gHRv2l0kOhBlrjRK7y-IpMbxE71Fne_lSzaptSuqgI5I9dFvpVfZWP1yMAqav8mrlUoWkWDq9IAkyH4bvvZrVgQJGgd5t9U_7DQCVwbkQvy0wGS5zoMcZhYenn_Ub1BoxWcviADQ1aY1wQju8OP0IOwKTIMXMQqciOFdJ9T5-tQEGUrikTu_tW-1shUHzOxBcEzGVtBvBy2OmbNnRFYogbhmp-Dze6EAi035bY32bfL7XKBUNCW6_3VbN_h3pQNAuT2NJOSKuhJ3cGldCB2zg")
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
w.Header().Set("Authorization", "xyz")
|
||||
w.Header().Set("ETag", "\"ABCDE\"")
|
||||
|
||||
type args struct {
|
||||
w http.ResponseWriter
|
||||
r *http.Request
|
||||
reqClaims map[string]interface{}
|
||||
deploymentID string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Entry
|
||||
preFunc func()
|
||||
postFunc func()
|
||||
}{
|
||||
{
|
||||
preFunc: func() {
|
||||
os.Setenv("CONSOLE_OPERATOR_MODE", "on")
|
||||
},
|
||||
postFunc: func() {
|
||||
os.Unsetenv("CONSOLE_OPERATOR_MODE")
|
||||
|
||||
},
|
||||
name: "constructs an audit entry from a http request",
|
||||
args: args{
|
||||
w: w,
|
||||
r: req,
|
||||
reqClaims: map[string]interface{}{},
|
||||
deploymentID: "1",
|
||||
},
|
||||
want: Entry{
|
||||
Version: "1",
|
||||
DeploymentID: "1",
|
||||
SessionID: "system:serviceaccount:minio-operator:console-sa",
|
||||
ReqQuery: map[string]string{"test": "xyz"},
|
||||
ReqHeader: map[string]string{"test": "xyz"},
|
||||
RespHeader: map[string]string{"test": "xyz", "ETag": "ABCDE"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.preFunc != nil {
|
||||
tt.preFunc()
|
||||
}
|
||||
if got := ToEntry(tt.args.w, tt.args.r, tt.args.reqClaims, tt.args.deploymentID); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("ToEntry() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if tt.postFunc != nil {
|
||||
tt.postFunc()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
64
pkg/logger/message/log/entry.go
Normal file
64
pkg/logger/message/log/entry.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 log
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// ObjectVersion object version key/versionId
|
||||
type ObjectVersion struct {
|
||||
ObjectName string `json:"objectName"`
|
||||
VersionID string `json:"versionId,omitempty"`
|
||||
}
|
||||
|
||||
// Args - defines the arguments for the API.
|
||||
type Args struct {
|
||||
Bucket string `json:"bucket,omitempty"`
|
||||
Object string `json:"object,omitempty"`
|
||||
VersionID string `json:"versionId,omitempty"`
|
||||
Objects []ObjectVersion `json:"objects,omitempty"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// Trace - defines the trace.
|
||||
type Trace struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
Source []string `json:"source,omitempty"`
|
||||
Variables map[string]interface{} `json:"variables,omitempty"`
|
||||
}
|
||||
|
||||
// API - defines the api type and its args.
|
||||
type API struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// Entry - defines fields and values of each log entry.
|
||||
type Entry struct {
|
||||
DeploymentID string `json:"deploymentid,omitempty"`
|
||||
Level string `json:"level"`
|
||||
LogKind string `json:"errKind"`
|
||||
Time time.Time `json:"time"`
|
||||
API *API `json:"api,omitempty"`
|
||||
RemoteHost string `json:"remotehost,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
RequestID string `json:"requestID,omitempty"`
|
||||
SessionID string `json:"sessionID,omitempty"`
|
||||
UserAgent string `json:"userAgent,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Trace *Trace `json:"errors,omitempty"`
|
||||
}
|
||||
117
pkg/logger/reqinfo.go
Normal file
117
pkg/logger/reqinfo.go
Normal file
@@ -0,0 +1,117 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
)
|
||||
|
||||
// KeyVal - appended to ReqInfo.Tags
|
||||
type KeyVal struct {
|
||||
Key string
|
||||
Val interface{}
|
||||
}
|
||||
|
||||
// ObjectVersion object version key/versionId
|
||||
type ObjectVersion struct {
|
||||
ObjectName string
|
||||
VersionID string `json:"VersionId,omitempty"`
|
||||
}
|
||||
|
||||
// ReqInfo stores the request info.
|
||||
type ReqInfo struct {
|
||||
RemoteHost string // Client Host/IP
|
||||
Host string // Node Host/IP
|
||||
UserAgent string // User Agent
|
||||
DeploymentID string // x-minio-deployment-id
|
||||
RequestID string // x-amz-request-id
|
||||
SessionID string // custom session id
|
||||
API string // API name - GetObject PutObject NewMultipartUpload etc.
|
||||
BucketName string `json:",omitempty"` // Bucket name
|
||||
ObjectName string `json:",omitempty"` // Object name
|
||||
VersionID string `json:",omitempty"` // corresponding versionID for the object
|
||||
Objects []ObjectVersion `json:",omitempty"` // Only set during MultiObject delete handler.
|
||||
AccessKey string // Access Key
|
||||
tags []KeyVal // Any additional info not accommodated by above fields
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// GetTags - returns the user defined tags
|
||||
func (r *ReqInfo) GetTags() []KeyVal {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
r.RLock()
|
||||
defer r.RUnlock()
|
||||
return append([]KeyVal(nil), r.tags...)
|
||||
}
|
||||
|
||||
// GetTagsMap - returns the user defined tags in a map structure
|
||||
func (r *ReqInfo) GetTagsMap() map[string]interface{} {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
r.RLock()
|
||||
defer r.RUnlock()
|
||||
m := make(map[string]interface{}, len(r.tags))
|
||||
for _, t := range r.tags {
|
||||
m[t.Key] = t.Val
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// SetReqInfo sets ReqInfo in the context.
|
||||
func SetReqInfo(ctx context.Context, req *ReqInfo) context.Context {
|
||||
if ctx == nil {
|
||||
LogIf(context.Background(), fmt.Errorf("context is nil"))
|
||||
return nil
|
||||
}
|
||||
return context.WithValue(ctx, utils.ContextLogKey, req)
|
||||
}
|
||||
|
||||
// GetReqInfo returns ReqInfo if set.
|
||||
func GetReqInfo(ctx context.Context) *ReqInfo {
|
||||
if ctx != nil {
|
||||
r, ok := ctx.Value(utils.ContextLogKey).(*ReqInfo)
|
||||
if ok {
|
||||
return r
|
||||
}
|
||||
r = &ReqInfo{}
|
||||
if val, o := ctx.Value(utils.ContextRequestID).(string); o {
|
||||
r.RequestID = val
|
||||
}
|
||||
if val, o := ctx.Value(utils.ContextRequestUserID).(string); o {
|
||||
r.SessionID = val
|
||||
}
|
||||
if val, o := ctx.Value(utils.ContextRequestUserAgent).(string); o {
|
||||
r.UserAgent = val
|
||||
}
|
||||
if val, o := ctx.Value(utils.ContextRequestHost).(string); o {
|
||||
r.Host = val
|
||||
}
|
||||
if val, o := ctx.Value(utils.ContextRequestRemoteAddr).(string); o {
|
||||
r.RemoteHost = val
|
||||
}
|
||||
SetReqInfo(ctx, r)
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
226
pkg/logger/target/http/http.go
Normal file
226
pkg/logger/target/http/http.go
Normal file
@@ -0,0 +1,226 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
"github.com/minio/console/pkg/logger/target/types"
|
||||
)
|
||||
|
||||
// Timeout for the webhook http call
|
||||
const webhookCallTimeout = 5 * time.Second
|
||||
|
||||
// Config http logger target
|
||||
type Config struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Name string `json:"name"`
|
||||
UserAgent string `json:"userAgent"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
AuthToken string `json:"authToken"`
|
||||
ClientCert string `json:"clientCert"`
|
||||
ClientKey string `json:"clientKey"`
|
||||
QueueSize int `json:"queueSize"`
|
||||
Transport http.RoundTripper `json:"-"`
|
||||
|
||||
// Custom logger
|
||||
LogOnce func(ctx context.Context, err error, id interface{}, errKind ...interface{}) `json:"-"`
|
||||
}
|
||||
|
||||
// Target implements logger.Target and sends the json
|
||||
// format of a log entry to the configured http endpoint.
|
||||
// An internal buffer of logs is maintained but when the
|
||||
// buffer is full, new logs are just ignored and an errors
|
||||
// is returned to the caller.
|
||||
type Target struct {
|
||||
status int32
|
||||
wg sync.WaitGroup
|
||||
|
||||
// Channel of log entries
|
||||
logCh chan interface{}
|
||||
|
||||
config Config
|
||||
}
|
||||
|
||||
// Endpoint returns the backend endpoint
|
||||
func (h *Target) Endpoint() string {
|
||||
return h.config.Endpoint
|
||||
}
|
||||
|
||||
func (h *Target) String() string {
|
||||
return h.config.Name
|
||||
}
|
||||
|
||||
// Init validate and initialize the http target
|
||||
func (h *Target) Init() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*webhookCallTimeout)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, h.config.Endpoint, strings.NewReader(`{}`))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set(xhttp.ContentType, "application/json")
|
||||
|
||||
// Set user-agent to indicate MinIO release
|
||||
// version to the configured log endpoint
|
||||
req.Header.Set("User-Agent", h.config.UserAgent)
|
||||
|
||||
if h.config.AuthToken != "" {
|
||||
req.Header.Set("Authorization", h.config.AuthToken)
|
||||
}
|
||||
|
||||
client := http.Client{Transport: h.config.Transport}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Drain any response.
|
||||
xhttp.DrainBody(resp.Body)
|
||||
|
||||
if !acceptedResponseStatusCode(resp.StatusCode) {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusForbidden:
|
||||
return fmt.Errorf("%s returned '%s', please check if your auth token is correctly set",
|
||||
h.config.Endpoint, resp.Status)
|
||||
}
|
||||
return fmt.Errorf("%s returned '%s', please check your endpoint configuration",
|
||||
h.config.Endpoint, resp.Status)
|
||||
}
|
||||
|
||||
h.status = 1
|
||||
go h.startHTTPLogger()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Accepted HTTP Status Codes
|
||||
var acceptedStatusCodeMap = map[int]bool{http.StatusOK: true, http.StatusCreated: true, http.StatusAccepted: true, http.StatusNoContent: true}
|
||||
|
||||
func acceptedResponseStatusCode(code int) bool {
|
||||
return acceptedStatusCodeMap[code]
|
||||
}
|
||||
|
||||
func (h *Target) logEntry(entry interface{}) {
|
||||
logJSON, err := json.Marshal(&entry)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), webhookCallTimeout)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
|
||||
h.config.Endpoint, bytes.NewReader(logJSON))
|
||||
if err != nil {
|
||||
h.config.LogOnce(ctx, fmt.Errorf("%s returned '%w', please check your endpoint configuration", h.config.Endpoint, err), h.config.Endpoint)
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
req.Header.Set(xhttp.ContentType, "application/json")
|
||||
|
||||
// Set user-agent to indicate MinIO release
|
||||
// version to the configured log endpoint
|
||||
req.Header.Set("User-Agent", h.config.UserAgent)
|
||||
|
||||
if h.config.AuthToken != "" {
|
||||
req.Header.Set("Authorization", h.config.AuthToken)
|
||||
}
|
||||
|
||||
client := http.Client{Transport: h.config.Transport}
|
||||
resp, err := client.Do(req)
|
||||
cancel()
|
||||
if err != nil {
|
||||
h.config.LogOnce(ctx, fmt.Errorf("%s returned '%w', please check your endpoint configuration", h.config.Endpoint, err), h.config.Endpoint)
|
||||
return
|
||||
}
|
||||
|
||||
// Drain any response.
|
||||
xhttp.DrainBody(resp.Body)
|
||||
|
||||
if !acceptedResponseStatusCode(resp.StatusCode) {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusForbidden:
|
||||
h.config.LogOnce(ctx, fmt.Errorf("%s returned '%s', please check if your auth token is correctly set", h.config.Endpoint, resp.Status), h.config.Endpoint)
|
||||
default:
|
||||
h.config.LogOnce(ctx, fmt.Errorf("%s returned '%s', please check your endpoint configuration", h.config.Endpoint, resp.Status), h.config.Endpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Target) startHTTPLogger() {
|
||||
// Create a routine which sends json logs received
|
||||
// from an internal channel.
|
||||
go func() {
|
||||
h.wg.Add(1)
|
||||
defer h.wg.Done()
|
||||
for entry := range h.logCh {
|
||||
h.logEntry(entry)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// New initializes a new logger target which
|
||||
// sends log over http to the specified endpoint
|
||||
func New(config Config) *Target {
|
||||
h := &Target{
|
||||
logCh: make(chan interface{}, config.QueueSize),
|
||||
config: config,
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// Send log message 'e' to http target.
|
||||
func (h *Target) Send(entry interface{}, errKind string) error {
|
||||
if atomic.LoadInt32(&h.status) == 0 {
|
||||
// Channel was closed or used before init.
|
||||
return nil
|
||||
}
|
||||
|
||||
select {
|
||||
case h.logCh <- entry:
|
||||
default:
|
||||
// log channel is full, do not wait and return
|
||||
// an errors immediately to the caller
|
||||
return errors.New("log buffer full")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cancel - cancels the target
|
||||
func (h *Target) Cancel() {
|
||||
if atomic.CompareAndSwapInt32(&h.status, 1, 0) {
|
||||
close(h.logCh)
|
||||
}
|
||||
h.wg.Wait()
|
||||
}
|
||||
|
||||
// Type - returns type of the target
|
||||
func (h *Target) Type() types.TargetType {
|
||||
return types.TargetHTTP
|
||||
}
|
||||
27
pkg/logger/target/types/types.go
Normal file
27
pkg/logger/target/types/types.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 types
|
||||
|
||||
// TargetType indicates type of the target e.g. console, http, kafka
|
||||
type TargetType uint8
|
||||
|
||||
// Constants for target types
|
||||
const (
|
||||
_ TargetType = iota
|
||||
TargetConsole
|
||||
TargetHTTP
|
||||
)
|
||||
151
pkg/logger/targets.go
Normal file
151
pkg/logger/targets.go
Normal file
@@ -0,0 +1,151 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 logger
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/minio/console/pkg/logger/target/http"
|
||||
"github.com/minio/console/pkg/logger/target/types"
|
||||
)
|
||||
|
||||
// Target is the entity that we will receive
|
||||
// a single log entry and Send it to the log target
|
||||
// e.g. Send the log to a http server
|
||||
type Target interface {
|
||||
String() string
|
||||
Endpoint() string
|
||||
Init() error
|
||||
Cancel()
|
||||
Send(entry interface{}, errKind string) error
|
||||
Type() types.TargetType
|
||||
}
|
||||
|
||||
var (
|
||||
// swapMu must be held while reading slice info or swapping targets or auditTargets.
|
||||
swapMu sync.Mutex
|
||||
|
||||
// systemTargets is the set of enabled loggers.
|
||||
// Must be immutable at all times.
|
||||
// Can be swapped to another while holding swapMu
|
||||
systemTargets = []Target{}
|
||||
|
||||
// This is always set represent /dev/console target
|
||||
consoleTgt Target
|
||||
|
||||
nTargets int32 // atomic count of len(targets)
|
||||
)
|
||||
|
||||
// SystemTargets returns active targets.
|
||||
// Returned slice may not be modified in any way.
|
||||
func SystemTargets() []Target {
|
||||
if atomic.LoadInt32(&nTargets) == 0 {
|
||||
// Lock free if none...
|
||||
return nil
|
||||
}
|
||||
swapMu.Lock()
|
||||
res := systemTargets
|
||||
swapMu.Unlock()
|
||||
return res
|
||||
}
|
||||
|
||||
// AuditTargets returns active audit targets.
|
||||
// Returned slice may not be modified in any way.
|
||||
func AuditTargets() []Target {
|
||||
if atomic.LoadInt32(&nAuditTargets) == 0 {
|
||||
// Lock free if none...
|
||||
return nil
|
||||
}
|
||||
swapMu.Lock()
|
||||
res := auditTargets
|
||||
swapMu.Unlock()
|
||||
return res
|
||||
}
|
||||
|
||||
// auditTargets is the list of enabled audit loggers
|
||||
// Must be immutable at all times.
|
||||
// Can be swapped to another while holding swapMu
|
||||
var (
|
||||
auditTargets = []Target{}
|
||||
nAuditTargets int32 // atomic count of len(auditTargets)
|
||||
)
|
||||
|
||||
func cancelAllSystemTargets() {
|
||||
for _, tgt := range systemTargets {
|
||||
tgt.Cancel()
|
||||
}
|
||||
}
|
||||
|
||||
func initSystemTargets(cfgMap map[string]http.Config) (tgts []Target, err error) {
|
||||
for _, l := range cfgMap {
|
||||
if l.Enabled {
|
||||
t := http.New(l)
|
||||
if err = t.Init(); err != nil {
|
||||
return tgts, err
|
||||
}
|
||||
tgts = append(tgts, t)
|
||||
}
|
||||
}
|
||||
return tgts, err
|
||||
}
|
||||
|
||||
// UpdateSystemTargets swaps targets with newly loaded ones from the cfg
|
||||
func UpdateSystemTargets(cfg Config) error {
|
||||
updated, err := initSystemTargets(cfg.HTTP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
swapMu.Lock()
|
||||
for _, tgt := range systemTargets {
|
||||
// Preserve console target when dynamically updating
|
||||
// other HTTP targets, console target is always present.
|
||||
if tgt.Type() == types.TargetConsole {
|
||||
updated = append(updated, tgt)
|
||||
break
|
||||
}
|
||||
}
|
||||
atomic.StoreInt32(&nTargets, int32(len(updated)))
|
||||
cancelAllSystemTargets() // cancel running targets
|
||||
systemTargets = updated
|
||||
swapMu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func cancelAuditTargetType(t types.TargetType) {
|
||||
for _, tgt := range auditTargets {
|
||||
if tgt.Type() == t {
|
||||
tgt.Cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateAuditWebhookTargets swaps audit webhook targets with newly loaded ones from the cfg
|
||||
func UpdateAuditWebhookTargets(cfg Config) error {
|
||||
updated, err := initSystemTargets(cfg.AuditWebhook)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
swapMu.Lock()
|
||||
atomic.StoreInt32(&nAuditTargets, int32(len(updated)))
|
||||
cancelAuditTargetType(types.TargetHTTP) // cancel running targets
|
||||
auditTargets = updated
|
||||
swapMu.Unlock()
|
||||
return nil
|
||||
}
|
||||
60
pkg/logger/utils.go
Normal file
60
pkg/logger/utils.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
||||
"github.com/minio/console/pkg/logger/color"
|
||||
)
|
||||
|
||||
var ansiRE = regexp.MustCompile("(\x1b[^m]*m)")
|
||||
|
||||
// Print ANSI Control escape
|
||||
func ansiEscape(format string, args ...interface{}) {
|
||||
Esc := "\x1b"
|
||||
fmt.Printf("%s%s", Esc, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func ansiMoveRight(n int) {
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
}
|
||||
if color.IsTerminal() {
|
||||
ansiEscape("[%dC", n)
|
||||
}
|
||||
}
|
||||
|
||||
func ansiSaveAttributes() {
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
}
|
||||
if color.IsTerminal() {
|
||||
ansiEscape("7")
|
||||
}
|
||||
}
|
||||
|
||||
func ansiRestoreAttributes() {
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
}
|
||||
if color.IsTerminal() {
|
||||
ansiEscape("8")
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user