Clean-up after PR 3509 (#3517)

This commit is contained in:
Ramon de Klein
2025-03-21 20:43:10 +01:00
committed by GitHub
parent 63c6d8952b
commit 71b1b708b7
15 changed files with 214 additions and 1787 deletions

View File

@@ -18,332 +18,18 @@ package api
import (
"context"
"io"
"time"
"github.com/minio/madmin-go/v3"
iampolicy "github.com/minio/pkg/v3/policy"
)
type AdminClientMock struct{}
var (
MinioServerInfoMock func(ctx context.Context) (madmin.InfoMessage, error)
minioChangePasswordMock func(ctx context.Context, accessKey, secretKey string) error
minioHelpConfigKVMock func(subSys, key string, envOnly bool) (madmin.Help, error)
minioGetConfigKVMock func(key string) ([]byte, error)
minioSetConfigKVMock func(kv string) (restart bool, err error)
minioDelConfigKVMock func(name string) (err error)
minioHelpConfigKVGlobalMock func(envOnly bool) (madmin.Help, error)
minioGetLogsMock func(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo
minioListGroupsMock func() ([]string, error)
minioUpdateGroupMembersMock func(madmin.GroupAddRemove) error
minioGetGroupDescriptionMock func(group string) (*madmin.GroupDesc, error)
minioSetGroupStatusMock func(group string, status madmin.GroupStatus) error
minioHealMock func(ctx context.Context, bucket, prefix string, healOpts madmin.HealOpts, clientToken string,
forceStart, forceStop bool) (healStart madmin.HealStartSuccess, healTaskStatus madmin.HealTaskStatus, err error)
minioServerHealthInfoMock func(ctx context.Context, deadline time.Duration) (interface{}, string, error)
minioListPoliciesMock func() (map[string]*iampolicy.Policy, error)
minioGetPolicyMock func(name string) (*iampolicy.Policy, error)
minioRemovePolicyMock func(name string) error
minioAddPolicyMock func(name string, policy *iampolicy.Policy) error
minioSetPolicyMock func(policyName, entityName string, isGroup bool) error
minioStartProfiling func(profiler madmin.ProfilerType, duration time.Duration) (io.ReadCloser, error)
minioServiceRestartMock func(ctx context.Context) error
getSiteReplicationInfo func(ctx context.Context) (*madmin.SiteReplicationInfo, error)
addSiteReplicationInfo func(ctx context.Context, sites []madmin.PeerSite) (*madmin.ReplicateAddStatus, error)
editSiteReplicationInfo func(ctx context.Context, site madmin.PeerInfo) (*madmin.ReplicateEditStatus, error)
deleteSiteReplicationInfoMock func(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error)
getSiteReplicationStatus func(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error)
minioListTiersMock func(ctx context.Context) ([]*madmin.TierConfig, error)
minioTierStatsMock func(ctx context.Context) ([]madmin.TierInfo, error)
minioAddTiersMock func(ctx context.Context, tier *madmin.TierConfig) error
minioRemoveTierMock func(ctx context.Context, tierName string) error
minioEditTiersMock func(ctx context.Context, tierName string, creds madmin.TierCreds) error
minioVerifyTierStatusMock func(ctx context.Context, tierName string) error
minioServiceTraceMock func(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo
minioListUsersMock func() (map[string]madmin.UserInfo, error)
minioAddUserMock func(accessKey, secreyKey string) error
minioRemoveUserMock func(accessKey string) error
minioGetUserInfoMock func(accessKey string) (madmin.UserInfo, error)
minioSetUserStatusMock func(accessKey string, status madmin.AccountStatus) error
minioAccountInfoMock func(ctx context.Context) (madmin.AccountInfo, error)
minioAddServiceAccountMock func(ctx context.Context, policy string, user string, accessKey string, secretKey string, description string, name string, expiry *time.Time, status string) (madmin.Credentials, error)
minioListServiceAccountsMock func(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error)
minioDeleteServiceAccountMock func(ctx context.Context, serviceAccount string) error
minioInfoServiceAccountMock func(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error)
minioUpdateServiceAccountMock func(ctx context.Context, serviceAccount string, opts madmin.UpdateServiceAccountReq) error
minioGetLDAPPolicyEntitiesMock func(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error)
minioListRemoteBucketsMock func(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error)
minioGetRemoteBucketMock func(ctx context.Context, bucket, arnType string) (targets *madmin.BucketTarget, err error)
minioAddRemoteBucketMock func(ctx context.Context, bucket string, target *madmin.BucketTarget) (string, error)
)
func (ac AdminClientMock) serverInfo(ctx context.Context) (madmin.InfoMessage, error) {
return MinioServerInfoMock(ctx)
}
func (ac AdminClientMock) listRemoteBuckets(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error) {
return minioListRemoteBucketsMock(ctx, bucket, arnType)
}
func (ac AdminClientMock) getRemoteBucket(ctx context.Context, bucket, arnType string) (targets *madmin.BucketTarget, err error) {
return minioGetRemoteBucketMock(ctx, bucket, arnType)
}
func (ac AdminClientMock) removeRemoteBucket(_ context.Context, _, _ string) error {
return nil
}
func (ac AdminClientMock) addRemoteBucket(ctx context.Context, bucket string, target *madmin.BucketTarget) (string, error) {
return minioAddRemoteBucketMock(ctx, bucket, target)
}
func (ac AdminClientMock) changePassword(ctx context.Context, accessKey, secretKey string) error {
return minioChangePasswordMock(ctx, accessKey, secretKey)
}
func (ac AdminClientMock) speedtest(_ context.Context, _ madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error) {
return nil, nil
}
func (ac AdminClientMock) verifyTierStatus(ctx context.Context, tier string) error {
return minioVerifyTierStatusMock(ctx, tier)
}
// mock function helpConfigKV()
func (ac AdminClientMock) helpConfigKV(_ context.Context, subSys, key string, envOnly bool) (madmin.Help, error) {
return minioHelpConfigKVMock(subSys, key, envOnly)
}
// mock function getConfigKV()
func (ac AdminClientMock) getConfigKV(_ context.Context, name string) ([]byte, error) {
return minioGetConfigKVMock(name)
}
// mock function setConfigKV()
func (ac AdminClientMock) setConfigKV(_ context.Context, kv string) (restart bool, err error) {
return minioSetConfigKVMock(kv)
}
// mock function helpConfigKV()
func (ac AdminClientMock) helpConfigKVGlobal(_ context.Context, envOnly bool) (madmin.Help, error) {
return minioHelpConfigKVGlobalMock(envOnly)
}
func (ac AdminClientMock) delConfigKV(_ context.Context, name string) (err error) {
return minioDelConfigKVMock(name)
}
func (ac AdminClientMock) getLogs(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo {
return minioGetLogsMock(ctx, node, lineCnt, logKind)
}
func (ac AdminClientMock) listGroups(_ context.Context) ([]string, error) {
return minioListGroupsMock()
}
func (ac AdminClientMock) updateGroupMembers(_ context.Context, req madmin.GroupAddRemove) error {
return minioUpdateGroupMembersMock(req)
}
func (ac AdminClientMock) getGroupDescription(_ context.Context, group string) (*madmin.GroupDesc, error) {
return minioGetGroupDescriptionMock(group)
}
func (ac AdminClientMock) setGroupStatus(_ context.Context, group string, status madmin.GroupStatus) error {
return minioSetGroupStatusMock(group, status)
}
func (ac AdminClientMock) heal(ctx context.Context, bucket, prefix string, healOpts madmin.HealOpts, clientToken string,
forceStart, forceStop bool,
) (healStart madmin.HealStartSuccess, healTaskStatus madmin.HealTaskStatus, err error) {
return minioHealMock(ctx, bucket, prefix, healOpts, clientToken, forceStart, forceStop)
}
func (ac AdminClientMock) serverHealthInfo(ctx context.Context, deadline time.Duration) (interface{}, string, error) {
return minioServerHealthInfoMock(ctx, deadline)
}
func (ac AdminClientMock) addOrUpdateIDPConfig(_ context.Context, _, _, _ string, _ bool) (restart bool, err error) {
return true, nil
}
func (ac AdminClientMock) listIDPConfig(_ context.Context, _ string) ([]madmin.IDPListItem, error) {
return []madmin.IDPListItem{{Name: "mock"}}, nil
}
func (ac AdminClientMock) deleteIDPConfig(_ context.Context, _, _ string) (restart bool, err error) {
return true, nil
}
func (ac AdminClientMock) getIDPConfig(_ context.Context, _, _ string) (c madmin.IDPConfig, err error) {
return madmin.IDPConfig{Info: []madmin.IDPCfgInfo{{Key: "mock", Value: "mock"}}}, nil
type AdminClientMock struct {
minioAccountInfoMock func(ctx context.Context) (madmin.AccountInfo, error)
}
func (ac AdminClientMock) kmsStatus(_ context.Context) (madmin.KMSStatus, error) {
return madmin.KMSStatus{Name: "name", DefaultKeyID: "key", Endpoints: map[string]madmin.ItemState{"localhost": madmin.ItemState("online")}}, nil
}
func (ac AdminClientMock) kmsAPIs(_ context.Context) ([]madmin.KMSAPI, error) {
return []madmin.KMSAPI{{Method: "GET", Path: "/mock"}}, nil
}
func (ac AdminClientMock) kmsMetrics(_ context.Context) (*madmin.KMSMetrics, error) {
return &madmin.KMSMetrics{}, nil
}
func (ac AdminClientMock) kmsVersion(_ context.Context) (*madmin.KMSVersion, error) {
return &madmin.KMSVersion{Version: "test-version"}, nil
}
func (ac AdminClientMock) createKey(_ context.Context, _ string) error {
return nil
}
func (ac AdminClientMock) listKeys(_ context.Context, _ string) ([]madmin.KMSKeyInfo, error) {
return []madmin.KMSKeyInfo{{
Name: "name",
CreatedBy: "by",
}}, nil
}
func (ac AdminClientMock) keyStatus(_ context.Context, _ string) (*madmin.KMSKeyStatus, error) {
return &madmin.KMSKeyStatus{KeyID: "key"}, nil
}
func (ac AdminClientMock) listPolicies(_ context.Context) (map[string]*iampolicy.Policy, error) {
return minioListPoliciesMock()
}
func (ac AdminClientMock) getPolicy(_ context.Context, name string) (*iampolicy.Policy, error) {
return minioGetPolicyMock(name)
}
func (ac AdminClientMock) removePolicy(_ context.Context, name string) error {
return minioRemovePolicyMock(name)
}
func (ac AdminClientMock) addPolicy(_ context.Context, name string, policy *iampolicy.Policy) error {
return minioAddPolicyMock(name, policy)
}
func (ac AdminClientMock) setPolicy(_ context.Context, policyName, entityName string, isGroup bool) error {
return minioSetPolicyMock(policyName, entityName, isGroup)
}
// mock function for startProfiling()
func (ac AdminClientMock) startProfiling(_ context.Context, profiler madmin.ProfilerType, duration time.Duration) (io.ReadCloser, error) {
return minioStartProfiling(profiler, duration)
}
// mock function of serviceRestart()
func (ac AdminClientMock) serviceRestart(ctx context.Context) error {
return minioServiceRestartMock(ctx)
}
func (ac AdminClientMock) getSiteReplicationInfo(ctx context.Context) (*madmin.SiteReplicationInfo, error) {
return getSiteReplicationInfo(ctx)
}
func (ac AdminClientMock) addSiteReplicationInfo(ctx context.Context, sites []madmin.PeerSite, _ madmin.SRAddOptions) (*madmin.ReplicateAddStatus, error) {
return addSiteReplicationInfo(ctx, sites)
}
func (ac AdminClientMock) editSiteReplicationInfo(ctx context.Context, site madmin.PeerInfo, _ madmin.SREditOptions) (*madmin.ReplicateEditStatus, error) {
return editSiteReplicationInfo(ctx, site)
}
func (ac AdminClientMock) deleteSiteReplicationInfo(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error) {
return deleteSiteReplicationInfoMock(ctx, removeReq)
}
func (ac AdminClientMock) getSiteReplicationStatus(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error) {
return getSiteReplicationStatus(ctx, params)
}
func (ac AdminClientMock) listTiers(ctx context.Context) ([]*madmin.TierConfig, error) {
return minioListTiersMock(ctx)
}
func (ac AdminClientMock) tierStats(ctx context.Context) ([]madmin.TierInfo, error) {
return minioTierStatsMock(ctx)
}
func (ac AdminClientMock) addTier(ctx context.Context, tier *madmin.TierConfig) error {
return minioAddTiersMock(ctx, tier)
}
func (ac AdminClientMock) removeTier(ctx context.Context, tierName string) error {
return minioRemoveTierMock(ctx, tierName)
}
func (ac AdminClientMock) editTierCreds(ctx context.Context, tierName string, creds madmin.TierCreds) error {
return minioEditTiersMock(ctx, tierName, creds)
}
func (ac AdminClientMock) serviceTrace(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo {
return minioServiceTraceMock(ctx, threshold, s3, internal, storage, os, errTrace)
}
func (ac AdminClientMock) listUsers(_ context.Context) (map[string]madmin.UserInfo, error) {
return minioListUsersMock()
}
func (ac AdminClientMock) addUser(_ context.Context, accessKey, secretKey string) error {
return minioAddUserMock(accessKey, secretKey)
}
func (ac AdminClientMock) removeUser(_ context.Context, accessKey string) error {
return minioRemoveUserMock(accessKey)
}
func (ac AdminClientMock) getUserInfo(_ context.Context, accessKey string) (madmin.UserInfo, error) {
return minioGetUserInfoMock(accessKey)
}
func (ac AdminClientMock) setUserStatus(_ context.Context, accessKey string, status madmin.AccountStatus) error {
return minioSetUserStatusMock(accessKey, status)
}
func (ac AdminClientMock) AccountInfo(ctx context.Context) (madmin.AccountInfo, error) {
return minioAccountInfoMock(ctx)
}
func (ac AdminClientMock) addServiceAccount(ctx context.Context, policy string, user string, accessKey string, secretKey string, description string, name string, expiry *time.Time, status string) (madmin.Credentials, error) {
return minioAddServiceAccountMock(ctx, policy, user, accessKey, secretKey, description, name, expiry, status)
}
func (ac AdminClientMock) listServiceAccounts(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error) {
return minioListServiceAccountsMock(ctx, user)
}
func (ac AdminClientMock) deleteServiceAccount(ctx context.Context, serviceAccount string) error {
return minioDeleteServiceAccountMock(ctx, serviceAccount)
}
func (ac AdminClientMock) infoServiceAccount(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error) {
return minioInfoServiceAccountMock(ctx, serviceAccount)
}
func (ac AdminClientMock) updateServiceAccount(ctx context.Context, serviceAccount string, opts madmin.UpdateServiceAccountReq) error {
return minioUpdateServiceAccountMock(ctx, serviceAccount, opts)
}
func (ac AdminClientMock) getLDAPPolicyEntities(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
return minioGetLDAPPolicyEntitiesMock(ctx, query)
return ac.minioAccountInfoMock(ctx)
}

View File

@@ -1,104 +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 api
import (
"context"
"encoding/json"
"strings"
"time"
"github.com/minio/madmin-go/v3"
"github.com/minio/websocket"
)
const logTimeFormat string = "15:04:05 MST 01/02/2006"
// startConsoleLog starts log of the servers
func startConsoleLog(ctx context.Context, conn WSConn, client MinioAdmin, logRequest LogRequest) error {
var node string
// name of node, default = "" (all)
if logRequest.node == "all" {
node = ""
} else {
node = logRequest.node
}
trimNode := strings.Split(node, ":")
// number of log lines
lineCount := 100
// type of logs "minio"|"application"|"all" default = "all"
var logKind string
if logRequest.logType == "minio" || logRequest.logType == "application" || logRequest.logType == "all" {
logKind = logRequest.logType
} else {
logKind = "all"
}
// Start listening on all Console Log activity.
logCh := client.getLogs(ctx, trimNode[0], lineCount, logKind)
for {
select {
case <-ctx.Done():
return nil
case logInfo, ok := <-logCh:
// zero value returned because the channel is closed and empty
if !ok {
return nil
}
if logInfo.Err != nil {
LogError("error on console logs: %v", logInfo.Err)
return logInfo.Err
}
// Serialize message to be sent
bytes, err := json.Marshal(serializeConsoleLogInfo(&logInfo))
if err != nil {
LogError("error on json.Marshal: %v", err)
return err
}
// Send Message through websocket connection
err = conn.writeMessage(websocket.TextMessage, bytes)
if err != nil {
LogError("error writeMessage: %v", err)
return err
}
}
}
}
func serializeConsoleLogInfo(l *madmin.LogInfo) (logInfo madmin.LogInfo) {
logInfo = *l
if logInfo.ConsoleMsg != "" {
logInfo.ConsoleMsg = strings.TrimPrefix(logInfo.ConsoleMsg, "\n")
}
if logInfo.Time != "" {
logInfo.Time = getLogTime(logInfo.Time)
}
return logInfo
}
func getLogTime(lt string) string {
tm, err := time.Parse(time.RFC3339Nano, lt)
if err != nil {
return lt
}
return tm.Format(logTimeFormat)
}

View File

@@ -1,117 +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 api
import (
"context"
"encoding/json"
"fmt"
"testing"
"github.com/minio/madmin-go/v3"
"github.com/stretchr/testify/assert"
)
func TestAdminConsoleLog(t *testing.T) {
assert := assert.New(t)
adminClient := AdminClientMock{}
mockWSConn := mockConn{}
function := "startConsoleLog(ctx, )"
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
testReceiver := make(chan madmin.LogInfo, 5)
textToReceive := "test message"
testStreamSize := 5
isClosed := false // testReceiver is closed?
// Test-1: Serve Console with no errors until Console finishes sending
// define mock function behavior for minio server Console
minioGetLogsMock = func(_ context.Context, _ string, _ int, _ string) <-chan madmin.LogInfo {
ch := make(chan madmin.LogInfo)
// Only success, start a routine to start reading line by line.
go func(ch chan<- madmin.LogInfo) {
defer close(ch)
lines := make([]int, testStreamSize)
// mocking sending 5 lines of info
for range lines {
info := madmin.LogInfo{
ConsoleMsg: textToReceive,
}
ch <- info
}
}(ch)
return ch
}
writesCount := 1
// mock connection WriteMessage() no error
connWriteMessageMock = func(_ int, data []byte) error {
// emulate that receiver gets the message written
var t madmin.LogInfo
_ = json.Unmarshal(data, &t)
if writesCount == testStreamSize {
if !isClosed {
close(testReceiver)
isClosed = true
}
return nil
}
testReceiver <- t
writesCount++
return nil
}
if err := startConsoleLog(ctx, mockWSConn, adminClient, LogRequest{node: "", logType: "all"}); err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
// check that the TestReceiver got the same number of data from Console.
for i := range testReceiver {
assert.Equal(textToReceive, i.ConsoleMsg)
}
// Test-2: if error happens while writing, return error
connWriteMessageMock = func(_ int, _ []byte) error {
return fmt.Errorf("error on write")
}
if err := startConsoleLog(ctx, mockWSConn, adminClient, LogRequest{node: "", logType: "all"}); assert.Error(err) {
assert.Equal("error on write", err.Error())
}
// Test-3: error happens on GetLogs Minio, Console should stop
// and error shall be returned.
minioGetLogsMock = func(_ context.Context, _ string, _ int, _ string) <-chan madmin.LogInfo {
ch := make(chan madmin.LogInfo)
// Only success, start a routine to start reading line by line.
go func(ch chan<- madmin.LogInfo) {
defer close(ch)
lines := make([]int, 2)
// mocking sending 5 lines of info
for range lines {
info := madmin.LogInfo{
ConsoleMsg: textToReceive,
}
ch <- info
}
ch <- madmin.LogInfo{Err: fmt.Errorf("error on Console")}
}(ch)
return ch
}
connWriteMessageMock = func(_ int, _ []byte) error {
return nil
}
if err := startConsoleLog(ctx, mockWSConn, adminClient, LogRequest{node: "", logType: "all"}); assert.Error(err) {
assert.Equal("error on Console", err.Error())
}
}

View File

@@ -1,51 +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 api
import (
"context"
"io"
"net/http"
"time"
"github.com/minio/madmin-go/v3"
"github.com/minio/websocket"
)
type profileOptions struct {
Types string
Duration time.Duration
}
func getProfileOptionsFromReq(req *http.Request) (*profileOptions, error) {
pOptions := profileOptions{}
pOptions.Types = req.FormValue("types")
pOptions.Duration = 10 * time.Second // TODO: make this configurable
return &pOptions, nil
}
func startProfiling(ctx context.Context, conn WSConn, client MinioAdmin, pOpts *profileOptions) error {
data, err := client.startProfiling(ctx, madmin.ProfilerType(pOpts.Types), pOpts.Duration)
if err != nil {
return err
}
message, err := io.ReadAll(data)
if err != nil {
return err
}
return conn.writeMessage(websocket.BinaryMessage, message)
}

View File

@@ -1,91 +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 api
import (
"bytes"
"context"
"errors"
"io"
"net/http"
"net/url"
"testing"
"time"
"github.com/minio/madmin-go/v3"
"github.com/stretchr/testify/assert"
)
// Implementing fake closingBuffer to mock stopProfiling() (io.ReadCloser, error)
type ClosingBuffer struct {
*bytes.Buffer
}
// Implementing a fake Close function for io.ReadCloser
func (cb *ClosingBuffer) Close() error {
return nil
}
func TestStartProfiling(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
assert := assert.New(t)
adminClient := AdminClientMock{}
mockWSConn := mockConn{}
function := "startProfiling()"
testOptions := &profileOptions{
Types: "cpu",
}
// Test-1 : startProfiling() Get response from MinIO server with one profiling object without errors
// mock function response from startProfiling()
minioStartProfiling = func(_ madmin.ProfilerType, _ time.Duration) (io.ReadCloser, error) {
return &ClosingBuffer{bytes.NewBufferString("In memory string eaeae")}, nil
}
// mock function response from mockConn.writeMessage()
connWriteMessageMock = func(_ int, _ []byte) error {
return nil
}
err := startProfiling(ctx, mockWSConn, adminClient, testOptions)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
assert.Equal(err, nil)
// Test-2 : startProfiling() Correctly handles errors returned by MinIO
// mock function response from startProfiling()
minioStartProfiling = func(_ madmin.ProfilerType, _ time.Duration) (io.ReadCloser, error) {
return nil, errors.New("error")
}
err = startProfiling(ctx, mockWSConn, adminClient, testOptions)
if assert.Error(err) {
assert.Equal("error", err.Error())
}
// Test-3: getProfileOptionsFromReq() correctly returns profile options from request
u, _ := url.Parse("ws://localhost/ws/profile?types=cpu,mem,block,mutex,trace,threads,goroutines")
req := &http.Request{
URL: u,
}
opts, err := getProfileOptionsFromReq(req)
if assert.NoError(err) {
expectedOptions := profileOptions{
Types: "cpu,mem,block,mutex,trace,threads,goroutines",
}
assert.Equal(expectedOptions.Types, opts.Types)
}
}

View File

@@ -1,153 +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 api
import (
"context"
"encoding/json"
"net/http"
"strings"
"time"
"github.com/minio/madmin-go/v3"
"github.com/minio/websocket"
)
// shortTraceMsg Short trace record
type shortTraceMsg struct {
Host string `json:"host"`
Time string `json:"time"`
Client string `json:"client"`
CallStats callStats `json:"callStats"`
FuncName string `json:"api"`
Path string `json:"path"`
Query string `json:"query"`
StatusCode int `json:"statusCode"`
StatusMsg string `json:"statusMsg"`
}
type callStats struct {
Rx int `json:"rx"`
Tx int `json:"tx"`
Duration string `json:"duration"`
Ttfb string `json:"timeToFirstByte"`
}
// trace filters
func matchTrace(opts TraceRequest, traceInfo madmin.ServiceTraceInfo) bool {
statusCode := int(opts.statusCode)
method := opts.method
funcName := opts.funcName
apiPath := opts.path
if statusCode == 0 && method == "" && funcName == "" && apiPath == "" {
// no specific filtering found trace all the requests
return true
}
// Filter request path if passed by the user
if apiPath != "" {
pathToLookup := strings.ToLower(apiPath)
pathFromTrace := strings.ToLower(traceInfo.Trace.Path)
return strings.Contains(pathFromTrace, pathToLookup)
}
// Filter response status codes if passed by the user
if statusCode > 0 && traceInfo.Trace.HTTP != nil {
statusCodeFromTrace := traceInfo.Trace.HTTP.RespInfo.StatusCode
return statusCodeFromTrace == statusCode
}
// Filter request method if passed by the user
if method != "" && traceInfo.Trace.HTTP != nil {
methodFromTrace := traceInfo.Trace.HTTP.ReqInfo.Method
return methodFromTrace == method
}
if funcName != "" {
funcToLookup := strings.ToLower(funcName)
funcFromTrace := strings.ToLower(traceInfo.Trace.FuncName)
return strings.Contains(funcFromTrace, funcToLookup)
}
return true
}
// startTraceInfo starts trace of the servers
func startTraceInfo(ctx context.Context, conn WSConn, client MinioAdmin, opts TraceRequest) error {
// Start listening on all trace activity.
traceCh := client.serviceTrace(ctx, opts.threshold, opts.s3, opts.internal, opts.storage, opts.os, opts.onlyErrors)
for {
select {
case <-ctx.Done():
return nil
case traceInfo, ok := <-traceCh:
// zero value returned because the channel is closed and empty
if !ok {
return nil
}
if traceInfo.Err != nil {
LogError("error on serviceTrace: %v", traceInfo.Err)
return traceInfo.Err
}
if matchTrace(opts, traceInfo) {
// Serialize message to be sent
traceInfoBytes, err := json.Marshal(shortTrace(&traceInfo))
if err != nil {
LogError("error on json.Marshal: %v", err)
return err
}
// Send Message through websocket connection
err = conn.writeMessage(websocket.TextMessage, traceInfoBytes)
if err != nil {
LogError("error writeMessage: %v", err)
return err
}
}
}
}
}
// shortTrace creates a shorter Trace Info message.
// Same implementation as github/minio/mc/cmd/admin-trace.go
func shortTrace(info *madmin.ServiceTraceInfo) shortTraceMsg {
t := info.Trace
s := shortTraceMsg{}
s.Time = t.Time.Format(time.RFC3339)
s.Path = t.Path
s.FuncName = t.FuncName
s.CallStats.Duration = t.Duration.String()
if info.Trace.HTTP != nil {
s.Query = t.HTTP.ReqInfo.RawQuery
s.StatusCode = t.HTTP.RespInfo.StatusCode
s.StatusMsg = http.StatusText(t.HTTP.RespInfo.StatusCode)
s.CallStats.Rx = t.HTTP.CallStats.InputBytes
s.CallStats.Tx = t.HTTP.CallStats.OutputBytes
s.CallStats.Ttfb = t.HTTP.CallStats.TimeToFirstByte.String()
if host, ok := t.HTTP.ReqInfo.Headers["Host"]; ok {
s.Host = strings.Join(host, "")
}
s.Client = t.HTTP.ReqInfo.Client
}
return s
}

View File

@@ -1,119 +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 api
import (
"context"
"encoding/json"
"fmt"
"testing"
"github.com/minio/madmin-go/v3"
"github.com/stretchr/testify/assert"
)
func TestAdminTrace(t *testing.T) {
assert := assert.New(t)
adminClient := AdminClientMock{}
mockWSConn := mockConn{}
function := "startTraceInfo(ctx, )"
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
testReceiver := make(chan shortTraceMsg, 5)
textToReceive := "test"
testStreamSize := 5
isClosed := false // testReceiver is closed?
// Test-1: Serve Trace with no errors until trace finishes sending
// define mock function behavior for minio server Trace
minioServiceTraceMock = func(_ context.Context, _ int64, _, _, _, _, _ bool) <-chan madmin.ServiceTraceInfo {
ch := make(chan madmin.ServiceTraceInfo)
// Only success, start a routine to start reading line by line.
go func(ch chan<- madmin.ServiceTraceInfo) {
defer close(ch)
lines := make([]int, testStreamSize)
// mocking sending 5 lines of info
for range lines {
info := madmin.TraceInfo{
FuncName: textToReceive,
}
ch <- madmin.ServiceTraceInfo{Trace: info}
}
}(ch)
return ch
}
writesCount := 1
// mock connection WriteMessage() no error
connWriteMessageMock = func(_ int, data []byte) error {
// emulate that receiver gets the message written
var t shortTraceMsg
_ = json.Unmarshal(data, &t)
if writesCount == testStreamSize {
// for testing we need to close the receiver channel
if !isClosed {
close(testReceiver)
isClosed = true
}
return nil
}
testReceiver <- t
writesCount++
return nil
}
if err := startTraceInfo(ctx, mockWSConn, adminClient, TraceRequest{s3: true, internal: true, storage: true, os: true, onlyErrors: false}); err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
// check that the TestReceiver got the same number of data from trace.
for i := range testReceiver {
assert.Equal(textToReceive, i.FuncName)
}
// Test-2: if error happens while writing, return error
connWriteMessageMock = func(_ int, _ []byte) error {
return fmt.Errorf("error on write")
}
if err := startTraceInfo(ctx, mockWSConn, adminClient, TraceRequest{}); assert.Error(err) {
assert.Equal("error on write", err.Error())
}
// Test-3: error happens on serviceTrace Minio, trace should stop
// and error shall be returned.
minioServiceTraceMock = func(_ context.Context, _ int64, _, _, _, _, _ bool) <-chan madmin.ServiceTraceInfo {
ch := make(chan madmin.ServiceTraceInfo)
// Only success, start a routine to start reading line by line.
go func(ch chan<- madmin.ServiceTraceInfo) {
defer close(ch)
lines := make([]int, 2)
// mocking sending 5 lines of info
for range lines {
info := madmin.TraceInfo{
NodeName: "test",
}
ch <- madmin.ServiceTraceInfo{Trace: info}
}
ch <- madmin.ServiceTraceInfo{Err: fmt.Errorf("error on trace")}
}(ch)
return ch
}
connWriteMessageMock = func(_ int, _ []byte) error {
return nil
}
if err := startTraceInfo(ctx, mockWSConn, adminClient, TraceRequest{}); assert.Error(err) {
assert.Equal("error on trace", err.Error())
}
}

View File

@@ -17,16 +17,12 @@
package api
import (
"bytes"
"context"
"encoding/json"
"io"
"net"
"net/http"
"net/url"
"regexp"
"strings"
"time"
"github.com/minio/console/pkg"
@@ -35,7 +31,6 @@ import (
"github.com/minio/console/models"
"github.com/minio/madmin-go/v3"
"github.com/minio/minio-go/v7/pkg/credentials"
iampolicy "github.com/minio/pkg/v3/policy"
)
const globalAppName = "MinIO Console"
@@ -44,87 +39,9 @@ const globalAppName = "MinIO Console"
// by mock when testing, it should include all MinioAdmin respective api calls
// that are used within this project.
type MinioAdmin interface {
listUsers(ctx context.Context) (map[string]madmin.UserInfo, error)
addUser(ctx context.Context, acessKey, SecretKey string) error
removeUser(ctx context.Context, accessKey string) error
getUserInfo(ctx context.Context, accessKey string) (madmin.UserInfo, error)
setUserStatus(ctx context.Context, accessKey string, status madmin.AccountStatus) error
listGroups(ctx context.Context) ([]string, error)
updateGroupMembers(ctx context.Context, greq madmin.GroupAddRemove) error
getGroupDescription(ctx context.Context, group string) (*madmin.GroupDesc, error)
setGroupStatus(ctx context.Context, group string, status madmin.GroupStatus) error
listPolicies(ctx context.Context) (map[string]*iampolicy.Policy, error)
getPolicy(ctx context.Context, name string) (*iampolicy.Policy, error)
removePolicy(ctx context.Context, name string) error
addPolicy(ctx context.Context, name string, policy *iampolicy.Policy) error
setPolicy(ctx context.Context, policyName, entityName string, isGroup bool) error
getConfigKV(ctx context.Context, key string) ([]byte, error)
helpConfigKV(ctx context.Context, subSys, key string, envOnly bool) (madmin.Help, error)
helpConfigKVGlobal(ctx context.Context, envOnly bool) (madmin.Help, error)
setConfigKV(ctx context.Context, kv string) (restart bool, err error)
delConfigKV(ctx context.Context, kv string) (err error)
serviceRestart(ctx context.Context) error
serverInfo(ctx context.Context) (madmin.InfoMessage, error)
startProfiling(ctx context.Context, profiler madmin.ProfilerType, duration time.Duration) (io.ReadCloser, error)
serviceTrace(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo
getLogs(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo
AccountInfo(ctx context.Context) (madmin.AccountInfo, error)
heal(ctx context.Context, bucket, prefix string, healOpts madmin.HealOpts, clientToken string,
forceStart, forceStop bool) (healStart madmin.HealStartSuccess, healTaskStatus madmin.HealTaskStatus, err error)
// Service Accounts
addServiceAccount(ctx context.Context, policy string, user string, accessKey string, secretKey string, name string, description string, expiry *time.Time, comment string) (madmin.Credentials, error)
listServiceAccounts(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error)
deleteServiceAccount(ctx context.Context, serviceAccount string) error
infoServiceAccount(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error)
updateServiceAccount(ctx context.Context, serviceAccount string, opts madmin.UpdateServiceAccountReq) error
// Remote Buckets
listRemoteBuckets(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error)
getRemoteBucket(ctx context.Context, bucket, arnType string) (targets *madmin.BucketTarget, err error)
removeRemoteBucket(ctx context.Context, bucket, arn string) error
addRemoteBucket(ctx context.Context, bucket string, target *madmin.BucketTarget) (string, error)
// Account password management
changePassword(ctx context.Context, accessKey, secretKey string) error
serverHealthInfo(ctx context.Context, deadline time.Duration) (interface{}, string, error)
// List Tiers
listTiers(ctx context.Context) ([]*madmin.TierConfig, error)
// Tier Info
tierStats(ctx context.Context) ([]madmin.TierInfo, error)
// Add Tier
addTier(ctx context.Context, tier *madmin.TierConfig) error
// Edit Tier Credentials
editTierCreds(ctx context.Context, tierName string, creds madmin.TierCreds) error
// verify Tier status
verifyTierStatus(ctx context.Context, tierName string) error
// remove empty Tier
removeTier(ctx context.Context, tierName string) error
// Speedtest
speedtest(ctx context.Context, opts madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error)
// Site Relication
getSiteReplicationInfo(ctx context.Context) (*madmin.SiteReplicationInfo, error)
addSiteReplicationInfo(ctx context.Context, sites []madmin.PeerSite, opts madmin.SRAddOptions) (*madmin.ReplicateAddStatus, error)
editSiteReplicationInfo(ctx context.Context, site madmin.PeerInfo, opts madmin.SREditOptions) (*madmin.ReplicateEditStatus, error)
deleteSiteReplicationInfo(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error)
// Replication status
getSiteReplicationStatus(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error)
// KMS
kmsStatus(ctx context.Context) (madmin.KMSStatus, error)
kmsMetrics(ctx context.Context) (*madmin.KMSMetrics, error)
kmsAPIs(ctx context.Context) ([]madmin.KMSAPI, error)
kmsVersion(ctx context.Context) (*madmin.KMSVersion, error)
createKey(ctx context.Context, key string) error
listKeys(ctx context.Context, pattern string) ([]madmin.KMSKeyInfo, error)
keyStatus(ctx context.Context, key string) (*madmin.KMSKeyStatus, error)
// IDP
addOrUpdateIDPConfig(ctx context.Context, idpType, cfgName, cfgData string, update bool) (restart bool, err error)
listIDPConfig(ctx context.Context, idpType string) ([]madmin.IDPListItem, error)
deleteIDPConfig(ctx context.Context, idpType, cfgName string) (restart bool, err error)
getIDPConfig(ctx context.Context, cfgType, cfgName string) (c madmin.IDPConfig, err error)
// LDAP
getLDAPPolicyEntities(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error)
}
// Interface implementation
@@ -135,292 +52,17 @@ type AdminClient struct {
Client *madmin.AdminClient
}
func (ac AdminClient) changePassword(ctx context.Context, accessKey, secretKey string) error {
return ac.Client.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
}
// implements madmin.ListUsers()
func (ac AdminClient) listUsers(ctx context.Context) (map[string]madmin.UserInfo, error) {
return ac.Client.ListUsers(ctx)
}
// implements madmin.AddUser()
func (ac AdminClient) addUser(ctx context.Context, accessKey, secretKey string) error {
return ac.Client.AddUser(ctx, accessKey, secretKey)
}
// implements madmin.RemoveUser()
func (ac AdminClient) removeUser(ctx context.Context, accessKey string) error {
return ac.Client.RemoveUser(ctx, accessKey)
}
// implements madmin.GetUserInfo()
func (ac AdminClient) getUserInfo(ctx context.Context, accessKey string) (madmin.UserInfo, error) {
return ac.Client.GetUserInfo(ctx, accessKey)
}
// implements madmin.SetUserStatus()
func (ac AdminClient) setUserStatus(ctx context.Context, accessKey string, status madmin.AccountStatus) error {
return ac.Client.SetUserStatus(ctx, accessKey, status)
}
// implements madmin.ListGroups()
func (ac AdminClient) listGroups(ctx context.Context) ([]string, error) {
return ac.Client.ListGroups(ctx)
}
// implements madmin.UpdateGroupMembers()
func (ac AdminClient) updateGroupMembers(ctx context.Context, greq madmin.GroupAddRemove) error {
return ac.Client.UpdateGroupMembers(ctx, greq)
}
// implements madmin.GetGroupDescription(group)
func (ac AdminClient) getGroupDescription(ctx context.Context, group string) (*madmin.GroupDesc, error) {
return ac.Client.GetGroupDescription(ctx, group)
}
// implements madmin.SetGroupStatus(group, status)
func (ac AdminClient) setGroupStatus(ctx context.Context, group string, status madmin.GroupStatus) error {
return ac.Client.SetGroupStatus(ctx, group, status)
}
// implements madmin.ListCannedPolicies()
func (ac AdminClient) listPolicies(ctx context.Context) (map[string]*iampolicy.Policy, error) {
policyMap, err := ac.Client.ListCannedPolicies(ctx)
if err != nil {
return nil, err
}
policies := make(map[string]*iampolicy.Policy, len(policyMap))
for k, v := range policyMap {
p, err := iampolicy.ParseConfig(bytes.NewReader(v))
if err != nil {
return nil, err
}
policies[k] = p
}
return policies, nil
}
// implements madmin.ListCannedPolicies()
func (ac AdminClient) getPolicy(ctx context.Context, name string) (*iampolicy.Policy, error) {
info, err := ac.Client.InfoCannedPolicyV2(ctx, name)
if err != nil {
return nil, err
}
return iampolicy.ParseConfig(bytes.NewReader(info.Policy))
}
// implements madmin.RemoveCannedPolicy()
func (ac AdminClient) removePolicy(ctx context.Context, name string) error {
return ac.Client.RemoveCannedPolicy(ctx, name)
}
// implements madmin.AddCannedPolicy()
func (ac AdminClient) addPolicy(ctx context.Context, name string, policy *iampolicy.Policy) error {
buf, err := json.Marshal(policy)
if err != nil {
return err
}
return ac.Client.AddCannedPolicy(ctx, name, buf)
}
// implements madmin.SetPolicy()
func (ac AdminClient) setPolicy(ctx context.Context, policyName, entityName string, isGroup bool) error {
// nolint:staticcheck // ignore SA1019
return ac.Client.SetPolicy(ctx, policyName, entityName, isGroup)
}
// implements madmin.GetConfigKV()
func (ac AdminClient) getConfigKV(ctx context.Context, key string) ([]byte, error) {
return ac.Client.GetConfigKV(ctx, key)
}
// implements madmin.HelpConfigKV()
func (ac AdminClient) helpConfigKV(ctx context.Context, subSys, key string, envOnly bool) (madmin.Help, error) {
return ac.Client.HelpConfigKV(ctx, subSys, key, envOnly)
}
// implements madmin.helpConfigKVGlobal()
func (ac AdminClient) helpConfigKVGlobal(ctx context.Context, envOnly bool) (madmin.Help, error) {
return ac.Client.HelpConfigKV(ctx, "", "", envOnly)
}
// implements madmin.SetConfigKV()
func (ac AdminClient) setConfigKV(ctx context.Context, kv string) (restart bool, err error) {
return ac.Client.SetConfigKV(ctx, kv)
}
// implements madmin.DelConfigKV()
func (ac AdminClient) delConfigKV(ctx context.Context, kv string) (err error) {
_, err = ac.Client.DelConfigKV(ctx, kv)
return err
}
// implements madmin.ServiceRestart()
func (ac AdminClient) serviceRestart(ctx context.Context) (err error) {
return ac.Client.ServiceRestartV2(ctx)
}
// implements madmin.ServerInfo()
func (ac AdminClient) serverInfo(ctx context.Context) (madmin.InfoMessage, error) {
return ac.Client.ServerInfo(ctx)
}
// implements madmin.StartProfiling()
func (ac AdminClient) startProfiling(ctx context.Context, profiler madmin.ProfilerType, duration time.Duration) (io.ReadCloser, error) {
return ac.Client.Profile(ctx, profiler, duration)
}
// implements madmin.ServiceTrace()
func (ac AdminClient) serviceTrace(ctx context.Context, threshold int64, _, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo {
thresholdT := time.Duration(threshold)
tracingOptions := madmin.ServiceTraceOpts{
S3: true,
OnlyErrors: errTrace,
Internal: internal,
Storage: storage,
OS: os,
Threshold: thresholdT,
}
return ac.Client.ServiceTrace(ctx, tracingOptions)
}
// implements madmin.GetLogs()
func (ac AdminClient) getLogs(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo {
return ac.Client.GetLogs(ctx, node, lineCnt, logKind)
}
// implements madmin.AddServiceAccount()
func (ac AdminClient) addServiceAccount(ctx context.Context, policy string, user string, accessKey string, secretKey string, name string, description string, expiry *time.Time, comment string) (madmin.Credentials, error) {
return ac.Client.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
Policy: []byte(policy),
TargetUser: user,
AccessKey: accessKey,
SecretKey: secretKey,
Name: name,
Description: description,
Expiration: expiry,
Comment: comment,
})
}
// implements madmin.ListServiceAccounts()
func (ac AdminClient) listServiceAccounts(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error) {
return ac.Client.ListServiceAccounts(ctx, user)
}
// implements madmin.DeleteServiceAccount()
func (ac AdminClient) deleteServiceAccount(ctx context.Context, serviceAccount string) error {
return ac.Client.DeleteServiceAccount(ctx, serviceAccount)
}
// implements madmin.InfoServiceAccount()
func (ac AdminClient) infoServiceAccount(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error) {
return ac.Client.InfoServiceAccount(ctx, serviceAccount)
}
// implements madmin.UpdateServiceAccount()
func (ac AdminClient) updateServiceAccount(ctx context.Context, serviceAccount string, opts madmin.UpdateServiceAccountReq) error {
return ac.Client.UpdateServiceAccount(ctx, serviceAccount, opts)
}
// AccountInfo implements madmin.AccountInfo()
func (ac AdminClient) AccountInfo(ctx context.Context) (madmin.AccountInfo, error) {
return ac.Client.AccountInfo(ctx, madmin.AccountOpts{})
}
func (ac AdminClient) heal(ctx context.Context, bucket, prefix string, healOpts madmin.HealOpts, clientToken string,
forceStart, forceStop bool,
) (healStart madmin.HealStartSuccess, healTaskStatus madmin.HealTaskStatus, err error) {
return ac.Client.Heal(ctx, bucket, prefix, healOpts, clientToken, forceStart, forceStop)
}
// listRemoteBuckets - return a list of remote buckets
func (ac AdminClient) listRemoteBuckets(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error) {
return ac.Client.ListRemoteTargets(ctx, bucket, arnType)
}
// getRemoteBucket - gets remote bucked based on a given bucket name
func (ac AdminClient) getRemoteBucket(ctx context.Context, bucket, arnType string) (*madmin.BucketTarget, error) {
targets, err := ac.Client.ListRemoteTargets(ctx, bucket, arnType)
if err != nil {
return nil, err
}
if len(targets) > 0 {
return &targets[0], nil
}
return nil, err
}
// removeRemoteBucket removes a remote target associated with particular ARN for this bucket
func (ac AdminClient) removeRemoteBucket(ctx context.Context, bucket, arn string) error {
return ac.Client.RemoveRemoteTarget(ctx, bucket, arn)
}
// addRemoteBucket sets up a remote target for this bucket
func (ac AdminClient) addRemoteBucket(ctx context.Context, bucket string, target *madmin.BucketTarget) (string, error) {
return ac.Client.SetRemoteTarget(ctx, bucket, target)
}
func (ac AdminClient) getBucketQuota(ctx context.Context, bucket string) (madmin.BucketQuota, error) {
return ac.Client.GetBucketQuota(ctx, bucket)
}
// serverHealthInfo implements mc.ServerHealthInfo - Connect to a minio server and call Health Info Management API
func (ac AdminClient) serverHealthInfo(ctx context.Context, deadline time.Duration) (interface{}, string, error) {
info := madmin.HealthInfo{}
var healthInfo interface{}
var version string
var resp *http.Response
var err error
resp, version, err = ac.Client.ServerHealthInfo(ctx, madmin.HealthDataTypesList, deadline, "")
if err != nil {
return nil, version, err
}
decoder := json.NewDecoder(resp.Body)
for {
if err = decoder.Decode(&info); err != nil {
break
}
}
if info.Version == "" {
return nil, "", ErrHealthReportFail
}
healthInfo = info
return healthInfo, version, nil
}
// implements madmin.listTiers()
func (ac AdminClient) listTiers(ctx context.Context) ([]*madmin.TierConfig, error) {
return ac.Client.ListTiers(ctx)
}
// implements madmin.tierStats()
func (ac AdminClient) tierStats(ctx context.Context) ([]madmin.TierInfo, error) {
return ac.Client.TierStats(ctx)
}
// implements madmin.AddTier()
func (ac AdminClient) addTier(ctx context.Context, cfg *madmin.TierConfig) error {
return ac.Client.AddTier(ctx, cfg)
}
// implements madmin.EditTier()
func (ac AdminClient) editTierCreds(ctx context.Context, tierName string, creds madmin.TierCreds) error {
return ac.Client.EditTier(ctx, tierName, creds)
}
// implements madmin.VerifyTier()
func (ac AdminClient) verifyTierStatus(ctx context.Context, tierName string) error {
return ac.Client.VerifyTier(ctx, tierName)
}
// implements madmin.RemoveTier()
func (ac AdminClient) removeTier(ctx context.Context, tierName string) error {
return ac.Client.RemoveTier(ctx, tierName)
func (ac AdminClient) kmsStatus(ctx context.Context) (madmin.KMSStatus, error) {
return ac.Client.KMSStatus(ctx)
}
func NewMinioAdminClient(ctx context.Context, sessionClaims *models.Principal) (*madmin.AdminClient, error) {
@@ -546,114 +188,3 @@ func getClientIP(r *http.Request) string {
}
return raddr
}
func (ac AdminClient) speedtest(ctx context.Context, opts madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error) {
return ac.Client.Speedtest(ctx, opts)
}
// Site Replication
func (ac AdminClient) getSiteReplicationInfo(ctx context.Context) (*madmin.SiteReplicationInfo, error) {
res, err := ac.Client.SiteReplicationInfo(ctx)
if err != nil {
return nil, err
}
return &madmin.SiteReplicationInfo{
Enabled: res.Enabled,
Name: res.Name,
Sites: res.Sites,
ServiceAccountAccessKey: res.ServiceAccountAccessKey,
}, nil
}
func (ac AdminClient) addSiteReplicationInfo(ctx context.Context, sites []madmin.PeerSite, opts madmin.SRAddOptions) (*madmin.ReplicateAddStatus, error) {
res, err := ac.Client.SiteReplicationAdd(ctx, sites, opts)
if err != nil {
return nil, err
}
return &madmin.ReplicateAddStatus{
Success: res.Success,
Status: res.Status,
ErrDetail: res.ErrDetail,
InitialSyncErrorMessage: res.InitialSyncErrorMessage,
}, nil
}
func (ac AdminClient) editSiteReplicationInfo(ctx context.Context, site madmin.PeerInfo, opts madmin.SREditOptions) (*madmin.ReplicateEditStatus, error) {
res, err := ac.Client.SiteReplicationEdit(ctx, site, opts)
if err != nil {
return nil, err
}
return &madmin.ReplicateEditStatus{
Success: res.Success,
Status: res.Status,
ErrDetail: res.ErrDetail,
}, nil
}
func (ac AdminClient) deleteSiteReplicationInfo(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error) {
res, err := ac.Client.SiteReplicationRemove(ctx, removeReq)
if err != nil {
return nil, err
}
return &madmin.ReplicateRemoveStatus{
Status: res.Status,
ErrDetail: res.ErrDetail,
}, nil
}
func (ac AdminClient) getSiteReplicationStatus(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error) {
res, err := ac.Client.SRStatusInfo(ctx, params)
if err != nil {
return nil, err
}
return &res, nil
}
func (ac AdminClient) kmsStatus(ctx context.Context) (madmin.KMSStatus, error) {
return ac.Client.KMSStatus(ctx)
}
func (ac AdminClient) kmsMetrics(ctx context.Context) (*madmin.KMSMetrics, error) {
return ac.Client.KMSMetrics(ctx)
}
func (ac AdminClient) kmsAPIs(ctx context.Context) ([]madmin.KMSAPI, error) {
return ac.Client.KMSAPIs(ctx)
}
func (ac AdminClient) kmsVersion(ctx context.Context) (*madmin.KMSVersion, error) {
return ac.Client.KMSVersion(ctx)
}
func (ac AdminClient) createKey(ctx context.Context, key string) error {
return ac.Client.CreateKey(ctx, key)
}
func (ac AdminClient) listKeys(ctx context.Context, pattern string) ([]madmin.KMSKeyInfo, error) {
return ac.Client.ListKeys(ctx, pattern)
}
func (ac AdminClient) keyStatus(ctx context.Context, key string) (*madmin.KMSKeyStatus, error) {
return ac.Client.GetKeyStatus(ctx, key)
}
func (ac AdminClient) addOrUpdateIDPConfig(ctx context.Context, idpType, cfgName, cfgData string, update bool) (restart bool, err error) {
return ac.Client.AddOrUpdateIDPConfig(ctx, idpType, cfgName, cfgData, update)
}
func (ac AdminClient) listIDPConfig(ctx context.Context, idpType string) ([]madmin.IDPListItem, error) {
return ac.Client.ListIDPConfig(ctx, idpType)
}
func (ac AdminClient) deleteIDPConfig(ctx context.Context, idpType, cfgName string) (restart bool, err error) {
return ac.Client.DeleteIDPConfig(ctx, idpType, cfgName)
}
func (ac AdminClient) getIDPConfig(ctx context.Context, idpType, cfgName string) (c madmin.IDPConfig, err error) {
return ac.Client.GetIDPConfig(ctx, idpType, cfgName)
}
func (ac AdminClient) getLDAPPolicyEntities(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
return ac.Client.GetLDAPPolicyEntities(ctx, query)
}

View File

@@ -40,109 +40,102 @@ import (
"github.com/stretchr/testify/assert"
)
// // Mock mc S3Client functions ////
var (
mcAddNotificationConfigMock func(ctx context.Context, arn string, events []string, prefix, suffix string, ignoreExisting bool) *probe.Error
mcRemoveNotificationConfigMock func(ctx context.Context, arn string, event string, prefix string, suffix string) *probe.Error
minioGetBucketNotificationMock func(ctx context.Context, bucketName string) (bucketNotification notification.Configuration, err error)
)
// Define a mock struct of mc S3Client interface implementation
type s3ClientMock struct{}
type s3ClientMock struct {
addNotificationConfigMock func(ctx context.Context, arn string, events []string, prefix, suffix string, ignoreExisting bool) *probe.Error
removeNotificationConfigMock func(ctx context.Context, arn string, event string, prefix string, suffix string) *probe.Error
setVersioningMock func(ctx context.Context, state string, excludePrefix []string, excludeFolders bool) *probe.Error
}
// implements mc.S3Client.AddNotificationConfigMock()
func (c s3ClientMock) addNotificationConfig(ctx context.Context, arn string, events []string, prefix, suffix string, ignoreExisting bool) *probe.Error {
return mcAddNotificationConfigMock(ctx, arn, events, prefix, suffix, ignoreExisting)
return c.addNotificationConfigMock(ctx, arn, events, prefix, suffix, ignoreExisting)
}
// implements mc.S3Client.DeleteBucketEventNotification()
func (c s3ClientMock) removeNotificationConfig(ctx context.Context, arn string, event string, prefix string, suffix string) *probe.Error {
return mcRemoveNotificationConfigMock(ctx, arn, event, prefix, suffix)
return c.removeNotificationConfigMock(ctx, arn, event, prefix, suffix)
}
func (c s3ClientMock) setVersioning(ctx context.Context, state string, excludePrefix []string, excludeFolders bool) *probe.Error {
return c.setVersioningMock(ctx, state, excludePrefix, excludeFolders)
}
// Define a mock struct of minio Client interface implementation
type minioClientMock struct {
getBucketNotificationMock func(ctx context.Context, bucketName string) (bucketNotification notification.Configuration, err error)
listBucketsWithContextMock func(ctx context.Context) ([]minio.BucketInfo, error)
makeBucketWithContextMock func(ctx context.Context, bucketName, location string, objectLock bool) error
setBucketPolicyWithContextMock func(ctx context.Context, bucketName, policy string) error
removeBucketMock func(bucketName string) error
getBucketPolicyMock func(bucketName string) (string, error)
setBucketEncryptionMock func(ctx context.Context, bucketName string, config *sse.Configuration) error
removeBucketEncryptionMock func(ctx context.Context, bucketName string) error
getBucketEncryptionMock func(ctx context.Context, bucketName string) (*sse.Configuration, error)
setObjectLockConfigMock func(ctx context.Context, bucketName string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit) error
getBucketObjectLockConfigMock func(ctx context.Context, bucketName string) (mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error)
getObjectLockConfigMock func(ctx context.Context, bucketName string) (lock string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error)
copyObjectMock func(ctx context.Context, dst minio.CopyDestOptions, src minio.CopySrcOptions) (minio.UploadInfo, error)
setBucketTaggingMock func(ctx context.Context, bucketName string, tags *tags.Tags) error
removeBucketTaggingMock func(ctx context.Context, bucketName string) error
}
// mock function of getBucketNotification()
func (mc minioClientMock) getBucketNotification(ctx context.Context, bucketName string) (bucketNotification notification.Configuration, err error) {
return minioGetBucketNotificationMock(ctx, bucketName)
return mc.getBucketNotificationMock(ctx, bucketName)
}
// assigning mock at runtime instead of compile time
var minioListBucketsWithContextMock func(ctx context.Context) ([]minio.BucketInfo, error)
var (
minioMakeBucketWithContextMock func(ctx context.Context, bucketName, location string, objectLock bool) error
minioSetBucketPolicyWithContextMock func(ctx context.Context, bucketName, policy string) error
minioRemoveBucketMock func(bucketName string) error
minioGetBucketPolicyMock func(bucketName string) (string, error)
minioSetBucketEncryptionMock func(ctx context.Context, bucketName string, config *sse.Configuration) error
minioRemoveBucketEncryptionMock func(ctx context.Context, bucketName string) error
minioGetBucketEncryptionMock func(ctx context.Context, bucketName string) (*sse.Configuration, error)
minioSetObjectLockConfigMock func(ctx context.Context, bucketName string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit) error
minioGetBucketObjectLockConfigMock func(ctx context.Context, bucketName string) (mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error)
minioGetObjectLockConfigMock func(ctx context.Context, bucketName string) (lock string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error)
minioSetVersioningMock func(ctx context.Context, state string, excludePrefix []string, excludeFolders bool) *probe.Error
minioCopyObjectMock func(ctx context.Context, dst minio.CopyDestOptions, src minio.CopySrcOptions) (minio.UploadInfo, error)
minioSetBucketTaggingMock func(ctx context.Context, bucketName string, tags *tags.Tags) error
minioRemoveBucketTaggingMock func(ctx context.Context, bucketName string) error
)
// Define a mock struct of minio Client interface implementation
type minioClientMock struct{}
// mock function of listBucketsWithContext()
func (mc minioClientMock) listBucketsWithContext(ctx context.Context) ([]minio.BucketInfo, error) {
return minioListBucketsWithContextMock(ctx)
return mc.listBucketsWithContextMock(ctx)
}
// mock function of makeBucketsWithContext()
func (mc minioClientMock) makeBucketWithContext(ctx context.Context, bucketName, location string, objectLock bool) error {
return minioMakeBucketWithContextMock(ctx, bucketName, location, objectLock)
return mc.makeBucketWithContextMock(ctx, bucketName, location, objectLock)
}
// mock function of setBucketPolicyWithContext()
func (mc minioClientMock) setBucketPolicyWithContext(ctx context.Context, bucketName, policy string) error {
return minioSetBucketPolicyWithContextMock(ctx, bucketName, policy)
return mc.setBucketPolicyWithContextMock(ctx, bucketName, policy)
}
// mock function of removeBucket()
func (mc minioClientMock) removeBucket(_ context.Context, bucketName string) error {
return minioRemoveBucketMock(bucketName)
return mc.removeBucketMock(bucketName)
}
// mock function of getBucketPolicy()
func (mc minioClientMock) getBucketPolicy(_ context.Context, bucketName string) (string, error) {
return minioGetBucketPolicyMock(bucketName)
return mc.getBucketPolicyMock(bucketName)
}
func (mc minioClientMock) setBucketEncryption(ctx context.Context, bucketName string, config *sse.Configuration) error {
return minioSetBucketEncryptionMock(ctx, bucketName, config)
return mc.setBucketEncryptionMock(ctx, bucketName, config)
}
func (mc minioClientMock) removeBucketEncryption(ctx context.Context, bucketName string) error {
return minioRemoveBucketEncryptionMock(ctx, bucketName)
return mc.removeBucketEncryptionMock(ctx, bucketName)
}
func (mc minioClientMock) getBucketEncryption(ctx context.Context, bucketName string) (*sse.Configuration, error) {
return minioGetBucketEncryptionMock(ctx, bucketName)
return mc.getBucketEncryptionMock(ctx, bucketName)
}
func (mc minioClientMock) setObjectLockConfig(ctx context.Context, bucketName string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit) error {
return minioSetObjectLockConfigMock(ctx, bucketName, mode, validity, unit)
return mc.setObjectLockConfigMock(ctx, bucketName, mode, validity, unit)
}
func (mc minioClientMock) getBucketObjectLockConfig(ctx context.Context, bucketName string) (mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error) {
return minioGetBucketObjectLockConfigMock(ctx, bucketName)
return mc.getBucketObjectLockConfigMock(ctx, bucketName)
}
func (mc minioClientMock) getObjectLockConfig(ctx context.Context, bucketName string) (lock string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error) {
return minioGetObjectLockConfigMock(ctx, bucketName)
return mc.getObjectLockConfigMock(ctx, bucketName)
}
func (mc minioClientMock) copyObject(ctx context.Context, dst minio.CopyDestOptions, src minio.CopySrcOptions) (minio.UploadInfo, error) {
return minioCopyObjectMock(ctx, dst, src)
}
func (c s3ClientMock) setVersioning(ctx context.Context, state string, excludePrefix []string, excludeFolders bool) *probe.Error {
return minioSetVersioningMock(ctx, state, excludePrefix, excludeFolders)
return mc.copyObjectMock(ctx, dst, src)
}
func (mc minioClientMock) GetBucketTagging(ctx context.Context, bucketName string) (*tags.Tags, error) {
@@ -150,11 +143,11 @@ func (mc minioClientMock) GetBucketTagging(ctx context.Context, bucketName strin
}
func (mc minioClientMock) SetBucketTagging(ctx context.Context, bucketName string, tags *tags.Tags) error {
return minioSetBucketTaggingMock(ctx, bucketName, tags)
return mc.setBucketTaggingMock(ctx, bucketName, tags)
}
func (mc minioClientMock) RemoveBucketTagging(ctx context.Context, bucketName string) error {
return minioRemoveBucketTaggingMock(ctx, bucketName)
return mc.removeBucketTaggingMock(ctx, bucketName)
}
func minioGetBucketTaggingMock(ctx context.Context, bucketName string) (*tags.Tags, error) {
@@ -172,7 +165,7 @@ func TestMakeBucket(t *testing.T) {
ctx := context.Background()
// Test-1: makeBucket() create a bucket
// mock function response from makeBucketWithContext(ctx)
minioMakeBucketWithContextMock = func(_ context.Context, _, _ string, _ bool) error {
minClient.makeBucketWithContextMock = func(_ context.Context, _, _ string, _ bool) error {
return nil
}
if err := makeBucket(ctx, minClient, "bucktest1", true); err != nil {
@@ -180,7 +173,7 @@ func TestMakeBucket(t *testing.T) {
}
// Test-2 makeBucket() make sure errors are handled correctly when errors on MakeBucketWithContext
minioMakeBucketWithContextMock = func(_ context.Context, _, _ string, _ bool) error {
minClient.makeBucketWithContextMock = func(_ context.Context, _, _ string, _ bool) error {
return errors.New("error")
}
if err := makeBucket(ctx, minClient, "bucktest1", true); assert.Error(err) {
@@ -199,7 +192,7 @@ func TestBucketInfo(t *testing.T) {
// Test-1: getBucketInfo() get a bucket with PRIVATE access
// if not policy set on bucket, access should be PRIVATE
mockPolicy := ""
minioGetBucketPolicyMock = func(_ string) (string, error) {
minClient.getBucketPolicyMock = func(_ string) (string, error) {
return mockPolicy, nil
}
bucketToSet := "csbucket"
@@ -241,7 +234,7 @@ func TestBucketInfo(t *testing.T) {
Policy: []byte(infoPolicy),
}
// mock function response from listBucketsWithContext(ctx)
minioAccountInfoMock = func(_ context.Context) (madmin.AccountInfo, error) {
adminClient.minioAccountInfoMock = func(_ context.Context) (madmin.AccountInfo, error) {
return mockBucketList, nil
}
@@ -258,7 +251,7 @@ func TestBucketInfo(t *testing.T) {
// Test-2: getBucketInfo() get a bucket with PUBLIC access
// mock policy for bucket csbucket with readWrite access (should return PUBLIC)
mockPolicy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::csbucket\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\",\"s3:DeleteObject\"],\"Resource\":[\"arn:aws:s3:::csbucket/*\"]}]}"
minioGetBucketPolicyMock = func(_ string) (string, error) {
minClient.getBucketPolicyMock = func(_ string) (string, error) {
return mockPolicy, nil
}
bucketToSet = "csbucket"
@@ -282,7 +275,7 @@ func TestBucketInfo(t *testing.T) {
// Test-3: getBucketInfo() get a bucket with PRIVATE access
// if bucket has a null statement, the bucket is PRIVATE
mockPolicy = "{\"Version\":\"2012-10-17\",\"Statement\":[]}"
minioGetBucketPolicyMock = func(_ string) (string, error) {
minClient.getBucketPolicyMock = func(_ string) (string, error) {
return mockPolicy, nil
}
bucketToSet = "csbucket"
@@ -305,7 +298,7 @@ func TestBucketInfo(t *testing.T) {
// Test-4: getBucketInfo() returns an errors while parsing invalid policy
mockPolicy = "policyinvalid"
minioGetBucketPolicyMock = func(_ string) (string, error) {
minClient.getBucketPolicyMock = func(_ string) (string, error) {
return mockPolicy, nil
}
bucketToSet = "csbucket"
@@ -333,7 +326,7 @@ func TestSetBucketAccess(t *testing.T) {
function := "setBucketAccessPolicy()"
// Test-1: setBucketAccessPolicy() set a bucket's access policy
// mock function response from setBucketPolicyWithContext(ctx)
minioSetBucketPolicyWithContextMock = func(_ context.Context, _, _ string) error {
minClient.setBucketPolicyWithContextMock = func(_ context.Context, _, _ string) error {
return nil
}
if err := setBucketAccessPolicy(ctx, minClient, "bucktest1", models.BucketAccessPUBLIC, ""); err != nil {
@@ -361,7 +354,7 @@ func TestSetBucketAccess(t *testing.T) {
}
// Test-5: setBucketAccessPolicy() handle errors on SetPolicy call
minioSetBucketPolicyWithContextMock = func(_ context.Context, _, _ string) error {
minClient.setBucketPolicyWithContextMock = func(_ context.Context, _, _ string) error {
return errors.New("error")
}
if err := setBucketAccessPolicy(ctx, minClient, "bucktest1", models.BucketAccessPUBLIC, ""); assert.Error(err) {
@@ -371,10 +364,8 @@ func TestSetBucketAccess(t *testing.T) {
func Test_enableBucketEncryption(t *testing.T) {
ctx := context.Background()
minClient := minioClientMock{}
type args struct {
ctx context.Context
client MinioClient
bucketName string
encryptionType models.BucketEncryptionType
kmsKeyID string
@@ -389,7 +380,6 @@ func Test_enableBucketEncryption(t *testing.T) {
name: "Bucket encryption enabled correctly",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
encryptionType: "sse-s3",
mockEnableBucketEncryptionFunc: func(_ context.Context, _ string, _ *sse.Configuration) error {
@@ -402,7 +392,6 @@ func Test_enableBucketEncryption(t *testing.T) {
name: "Error when enabling bucket encryption",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
encryptionType: "sse-s3",
mockEnableBucketEncryptionFunc: func(_ context.Context, _ string, _ *sse.Configuration) error {
@@ -414,8 +403,9 @@ func Test_enableBucketEncryption(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(_ *testing.T) {
minioSetBucketEncryptionMock = tt.args.mockEnableBucketEncryptionFunc
if err := enableBucketEncryption(tt.args.ctx, tt.args.client, tt.args.bucketName, tt.args.encryptionType, tt.args.kmsKeyID); (err != nil) != tt.wantErr {
minClient := minioClientMock{}
minClient.setBucketEncryptionMock = tt.args.mockEnableBucketEncryptionFunc
if err := enableBucketEncryption(tt.args.ctx, minClient, tt.args.bucketName, tt.args.encryptionType, tt.args.kmsKeyID); (err != nil) != tt.wantErr {
t.Errorf("enableBucketEncryption() errors = %v, wantErr %v", err, tt.wantErr)
}
})
@@ -424,10 +414,8 @@ func Test_enableBucketEncryption(t *testing.T) {
func Test_disableBucketEncryption(t *testing.T) {
ctx := context.Background()
minClient := minioClientMock{}
type args struct {
ctx context.Context
client MinioClient
bucketName string
mockBucketDisableFunc func(ctx context.Context, bucketName string) error
}
@@ -440,7 +428,6 @@ func Test_disableBucketEncryption(t *testing.T) {
name: "Bucket encryption disabled correctly",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
mockBucketDisableFunc: func(_ context.Context, _ string) error {
return nil
@@ -452,7 +439,6 @@ func Test_disableBucketEncryption(t *testing.T) {
name: "Error when disabling bucket encryption",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
mockBucketDisableFunc: func(_ context.Context, _ string) error {
return ErrDefault
@@ -463,8 +449,9 @@ func Test_disableBucketEncryption(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(_ *testing.T) {
minioRemoveBucketEncryptionMock = tt.args.mockBucketDisableFunc
if err := disableBucketEncryption(tt.args.ctx, tt.args.client, tt.args.bucketName); (err != nil) != tt.wantErr {
minClient := minioClientMock{}
minClient.removeBucketEncryptionMock = tt.args.mockBucketDisableFunc
if err := disableBucketEncryption(tt.args.ctx, minClient, tt.args.bucketName); (err != nil) != tt.wantErr {
t.Errorf("disableBucketEncryption() errors = %v, wantErr %v", err, tt.wantErr)
}
})
@@ -473,10 +460,8 @@ func Test_disableBucketEncryption(t *testing.T) {
func Test_getBucketEncryptionInfo(t *testing.T) {
ctx := context.Background()
minClient := minioClientMock{}
type args struct {
ctx context.Context
client MinioClient
bucketName string
mockBucketEncryptionGet func(ctx context.Context, bucketName string) (*sse.Configuration, error)
}
@@ -490,7 +475,6 @@ func Test_getBucketEncryptionInfo(t *testing.T) {
name: "Bucket encryption info returned correctly",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
mockBucketEncryptionGet: func(_ context.Context, _ string) (*sse.Configuration, error) {
return &sse.Configuration{
@@ -512,7 +496,6 @@ func Test_getBucketEncryptionInfo(t *testing.T) {
name: "Bucket encryption info with no rules",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
mockBucketEncryptionGet: func(_ context.Context, _ string) (*sse.Configuration, error) {
return &sse.Configuration{
@@ -526,7 +509,6 @@ func Test_getBucketEncryptionInfo(t *testing.T) {
name: "Error when obtaining bucket encryption info",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
mockBucketEncryptionGet: func(_ context.Context, _ string) (*sse.Configuration, error) {
return nil, ErrSSENotConfigured
@@ -537,8 +519,9 @@ func Test_getBucketEncryptionInfo(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(_ *testing.T) {
minioGetBucketEncryptionMock = tt.args.mockBucketEncryptionGet
got, err := getBucketEncryptionInfo(tt.args.ctx, tt.args.client, tt.args.bucketName)
minClient := minioClientMock{}
minClient.getBucketEncryptionMock = tt.args.mockBucketEncryptionGet
got, err := getBucketEncryptionInfo(tt.args.ctx, minClient, tt.args.bucketName)
if (err != nil) != tt.wantErr {
t.Errorf("getBucketEncryptionInfo() errors = %v, wantErr %v", err, tt.wantErr)
return
@@ -553,10 +536,8 @@ func Test_getBucketEncryptionInfo(t *testing.T) {
func Test_SetBucketRetentionConfig(t *testing.T) {
assert := assert.New(t)
ctx := context.Background()
minClient := minioClientMock{}
type args struct {
ctx context.Context
client MinioClient
bucketName string
mode models.ObjectRetentionMode
unit models.ObjectRetentionUnit
@@ -572,7 +553,6 @@ func Test_SetBucketRetentionConfig(t *testing.T) {
name: "Set Bucket Retention Config",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
mode: models.ObjectRetentionModeCompliance,
unit: models.ObjectRetentionUnitDays,
@@ -587,7 +567,6 @@ func Test_SetBucketRetentionConfig(t *testing.T) {
name: "Set Bucket Retention Config 2",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
mode: models.ObjectRetentionModeGovernance,
unit: models.ObjectRetentionUnitYears,
@@ -602,7 +581,6 @@ func Test_SetBucketRetentionConfig(t *testing.T) {
name: "Invalid validity",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
mode: models.ObjectRetentionModeCompliance,
unit: models.ObjectRetentionUnitDays,
@@ -617,7 +595,6 @@ func Test_SetBucketRetentionConfig(t *testing.T) {
name: "Invalid retention mode",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
mode: models.ObjectRetentionMode("othermode"),
unit: models.ObjectRetentionUnitDays,
@@ -632,7 +609,6 @@ func Test_SetBucketRetentionConfig(t *testing.T) {
name: "Invalid retention unit",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
mode: models.ObjectRetentionModeCompliance,
unit: models.ObjectRetentionUnit("otherunit"),
@@ -647,7 +623,6 @@ func Test_SetBucketRetentionConfig(t *testing.T) {
name: "Handle errors on objec lock function",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
mode: models.ObjectRetentionModeCompliance,
unit: models.ObjectRetentionUnitDays,
@@ -661,8 +636,9 @@ func Test_SetBucketRetentionConfig(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(_ *testing.T) {
minioSetObjectLockConfigMock = tt.args.mockBucketRetentionFunc
err := setBucketRetentionConfig(tt.args.ctx, tt.args.client, tt.args.bucketName, tt.args.mode, tt.args.unit, tt.args.validity)
minClient := minioClientMock{}
minClient.setObjectLockConfigMock = tt.args.mockBucketRetentionFunc
err := setBucketRetentionConfig(tt.args.ctx, minClient, tt.args.bucketName, tt.args.mode, tt.args.unit, tt.args.validity)
if tt.expectedError != nil {
fmt.Println(t.Name())
assert.Equal(tt.expectedError.Error(), err.Error(), fmt.Sprintf("setObjectRetention() errors: `%s`, wantErr: `%s`", err, tt.expectedError))
@@ -676,10 +652,8 @@ func Test_SetBucketRetentionConfig(t *testing.T) {
func Test_GetBucketRetentionConfig(t *testing.T) {
assert := assert.New(t)
ctx := context.Background()
minClient := minioClientMock{}
type args struct {
ctx context.Context
client MinioClient
bucketName string
getRetentionFunc func(ctx context.Context, bucketName string) (mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error)
}
@@ -693,7 +667,6 @@ func Test_GetBucketRetentionConfig(t *testing.T) {
name: "Get Bucket Retention Config",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
getRetentionFunc: func(_ context.Context, _ string) (mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error) {
m := minio.Governance
@@ -712,7 +685,6 @@ func Test_GetBucketRetentionConfig(t *testing.T) {
name: "Get Bucket Retention Config Compliance",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
getRetentionFunc: func(_ context.Context, _ string) (mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error) {
m := minio.Compliance
@@ -731,7 +703,6 @@ func Test_GetBucketRetentionConfig(t *testing.T) {
name: "Handle Error on minio func",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
getRetentionFunc: func(_ context.Context, _ string) (mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error) {
return nil, nil, nil, errors.New("error func")
@@ -746,7 +717,6 @@ func Test_GetBucketRetentionConfig(t *testing.T) {
name: "Handle NoLock Config errors",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
getRetentionFunc: func(_ context.Context, _ string) (mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error) {
return nil, nil, nil, minio.ErrorResponse{
@@ -762,7 +732,6 @@ func Test_GetBucketRetentionConfig(t *testing.T) {
name: "Return errors on invalid mode",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
getRetentionFunc: func(_ context.Context, _ string) (mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error) {
m := minio.RetentionMode("other")
@@ -777,7 +746,6 @@ func Test_GetBucketRetentionConfig(t *testing.T) {
name: "Return errors on invalid unit",
args: args{
ctx: ctx,
client: minClient,
bucketName: "test",
getRetentionFunc: func(_ context.Context, _ string) (mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error) {
m := minio.Governance
@@ -792,8 +760,9 @@ func Test_GetBucketRetentionConfig(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(_ *testing.T) {
minioGetBucketObjectLockConfigMock = tt.args.getRetentionFunc
resp, err := getBucketRetentionConfig(tt.args.ctx, tt.args.client, tt.args.bucketName)
minClient := minioClientMock{}
minClient.getBucketObjectLockConfigMock = tt.args.getRetentionFunc
resp, err := getBucketRetentionConfig(tt.args.ctx, minClient, tt.args.bucketName)
if tt.expectedError != nil {
fmt.Println(t.Name())
@@ -813,14 +782,12 @@ func Test_SetBucketVersioning(t *testing.T) {
assert := assert.New(t)
ctx := context.WithValue(context.Background(), utils.ContextClientIP, "127.0.0.1")
errorMsg := "Error Message"
minClient := s3ClientMock{}
type args struct {
ctx context.Context
state VersionState
excludePrefix []string
excludeFolders bool
bucketName string
client s3ClientMock
setVersioningFunc func(ctx context.Context, state string, excludePrefix []string, excludeFolders bool) *probe.Error
}
tests := []struct {
@@ -834,7 +801,6 @@ func Test_SetBucketVersioning(t *testing.T) {
ctx: ctx,
state: VersionEnable,
bucketName: "test",
client: minClient,
setVersioningFunc: func(_ context.Context, _ string, _ []string, _ bool) *probe.Error {
return nil
},
@@ -848,7 +814,6 @@ func Test_SetBucketVersioning(t *testing.T) {
state: VersionEnable,
excludePrefix: []string{"prefix1", "prefix2"},
bucketName: "test",
client: minClient,
setVersioningFunc: func(_ context.Context, _ string, _ []string, _ bool) *probe.Error {
return nil
},
@@ -863,7 +828,6 @@ func Test_SetBucketVersioning(t *testing.T) {
excludePrefix: []string{"prefix1", "prefix2"},
excludeFolders: true,
bucketName: "test",
client: minClient,
setVersioningFunc: func(_ context.Context, _ string, _ []string, _ bool) *probe.Error {
return nil
},
@@ -876,7 +840,6 @@ func Test_SetBucketVersioning(t *testing.T) {
ctx: ctx,
state: VersionEnable,
bucketName: "test",
client: minClient,
setVersioningFunc: func(_ context.Context, _ string, _ []string, _ bool) *probe.Error {
return probe.NewError(errors.New(errorMsg))
},
@@ -887,9 +850,10 @@ func Test_SetBucketVersioning(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(_ *testing.T) {
minioSetVersioningMock = tt.args.setVersioningFunc
s3Client := s3ClientMock{}
s3Client.setVersioningMock = tt.args.setVersioningFunc
err := doSetVersioning(tt.args.ctx, tt.args.client, tt.args.state, tt.args.excludePrefix, tt.args.excludeFolders)
err := doSetVersioning(tt.args.ctx, s3Client, tt.args.state, tt.args.excludePrefix, tt.args.excludeFolders)
fmt.Println(t.Name())
fmt.Println("Expected:", tt.expectedError, "Error:", err)
@@ -1202,11 +1166,11 @@ func Test_getAccountBuckets(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(_ *testing.T) {
client := AdminClientMock{}
// mock function response from listBucketsWithContext(ctx)
minioAccountInfoMock = func(_ context.Context) (madmin.AccountInfo, error) {
client.minioAccountInfoMock = func(_ context.Context) (madmin.AccountInfo, error) {
return tt.args.mockBucketList, tt.args.mockError
}
client := AdminClientMock{}
got, err := getAccountBuckets(tt.args.ctx, client)
if !tt.wantErr(t, err, fmt.Sprintf("getAccountBuckets(%v, %v)", tt.args.ctx, client)) {

View File

@@ -149,28 +149,25 @@ func Test_validateUserAgainstIDP(t *testing.T) {
}
func Test_getAccountInfo(t *testing.T) {
client := AdminClientMock{}
type args struct {
ctx context.Context
client MinioAdmin
ctx context.Context
}
tests := []struct {
name string
args args
want *iampolicy.Policy
wantErr bool
mockFunc func()
mockFunc func(client *AdminClientMock)
}{
{
name: "error getting account info",
args: args{
ctx: context.Background(),
client: client,
ctx: context.Background(),
},
want: nil,
wantErr: true,
mockFunc: func() {
minioAccountInfoMock = func(_ context.Context) (madmin.AccountInfo, error) {
mockFunc: func(client *AdminClientMock) {
client.minioAccountInfoMock = func(_ context.Context) (madmin.AccountInfo, error) {
return madmin.AccountInfo{}, errors.New("something went wrong")
}
},
@@ -178,10 +175,11 @@ func Test_getAccountInfo(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(_ *testing.T) {
client := AdminClientMock{}
if tt.mockFunc != nil {
tt.mockFunc()
tt.mockFunc(&client)
}
got, err := getAccountInfo(tt.args.ctx, tt.args.client)
got, err := getAccountInfo(tt.args.ctx, client)
if (err != nil) != tt.wantErr {
t.Errorf("getAccountInfo() error = %v, wantErr %v", err, tt.wantErr)
return

View File

@@ -42,29 +42,6 @@ const (
wsBasePath = "/ws"
)
// ConsoleWebsocketAdmin interface of a Websocket Client
type ConsoleWebsocketAdmin interface {
trace()
console()
}
type wsAdminClient struct {
// websocket connection.
conn wsConn
// MinIO admin Client
client MinioAdmin
}
// ConsoleWebsocket interface of a Websocket Client
type ConsoleWebsocket interface {
watch(options watchOptions)
}
// ConsoleWebSocketMClient interface of a Websocket Client
type ConsoleWebsocketMClient interface {
objectManager(options objectsListOpts)
}
type wsMinioClient struct {
// websocket connection.
conn wsConn
@@ -89,26 +66,6 @@ type wsConn struct {
conn *websocket.Conn
}
// Types for trace request. this adds support for calls, threshold, status and extra filters
type TraceRequest struct {
s3 bool
internal bool
storage bool
os bool
threshold int64
onlyErrors bool
statusCode int64
method string
funcName string
path string
}
// Type for log requests. This allows for filtering by node and kind
type LogRequest struct {
node string
logType string
}
func (c wsConn) writeMessage(messageType int, data []byte) error {
return c.conn.WriteMessage(messageType, data)
}
@@ -176,22 +133,6 @@ func serveWS(w http.ResponseWriter, req *http.Request) {
}
switch {
case strings.HasPrefix(wsPath, `/console`):
wsAdminClient, err := newWebSocketAdminClient(conn, session, clientIP)
if err != nil {
ErrorWithContext(ctx, err)
closeWsConn(conn)
return
}
node := req.URL.Query().Get("node")
logType := req.URL.Query().Get("logType")
logRequestItem := LogRequest{
node: node,
logType: logType,
}
go wsAdminClient.console(ctx, logRequestItem)
case strings.HasPrefix(wsPath, `/objectManager`):
wsMinioClient, err := newWebSocketMinioClient(conn, session, clientIP)
if err != nil {
@@ -207,28 +148,6 @@ func serveWS(w http.ResponseWriter, req *http.Request) {
}
}
// newWebSocketAdminClient returns a wsAdminClient authenticated as an admin user
func newWebSocketAdminClient(conn *websocket.Conn, autClaims *models.Principal, clientIP string) (*wsAdminClient, error) {
// create a websocket connection interface implementation
// defining the connection to be used
wsConnection := wsConn{conn: conn}
// Only start Websocket Interaction after user has been
// authenticated with MinIO
mAdmin, err := newAdminFromClaims(autClaims, clientIP)
if err != nil {
LogError("error creating madmin client: %v", err)
return nil, err
}
// create a minioClient interface implementation
// defining the client to be used
adminClient := AdminClient{Client: mAdmin}
// create websocket client and handle request
wsAdminClient := &wsAdminClient{conn: wsConnection, client: adminClient}
return wsAdminClient, nil
}
func newWebSocketMinioClient(conn *websocket.Conn, claims *models.Principal, clientIP string) (*wsMinioClient, error) {
mClient, err := newMinioClient(claims, clientIP)
if err != nil {
@@ -312,42 +231,3 @@ func closeWsConn(conn *websocket.Conn) {
conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
conn.Close()
}
// console serves madmin.GetLogs
// on a Websocket connection.
func (wsc *wsAdminClient) console(ctx context.Context, logRequestItem LogRequest) {
defer func() {
LogInfo("console logs stopped")
// close connection after return
wsc.conn.close()
}()
LogInfo("console logs started")
ctx = wsReadClientCtx(ctx, wsc.conn)
err := startConsoleLog(ctx, wsc.conn, wsc.client, logRequestItem)
sendWsCloseMessage(wsc.conn, err)
}
// sendWsCloseMessage sends Websocket Connection Close Message indicating the Status Code
// see https://tools.ietf.org/html/rfc6455#page-45
func sendWsCloseMessage(conn WSConn, err error) {
if err != nil {
LogError("original ws error: %v", err)
// If connection exceeded read deadline send Close
// Message Policy Violation code since we don't want
// to let the receiver figure out the read deadline.
// This is a generic code designed if there is a
// need to hide specific details about the policy.
if nErr, ok := err.(net.Error); ok && nErr.Timeout() {
conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.ClosePolicyViolation, ""))
return
}
// else, internal server error
conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseInternalServerErr, err.Error()))
return
}
// normal closure
conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
}

74
go.mod
View File

@@ -1,12 +1,12 @@
module github.com/minio/console
go 1.23
go 1.23.0
toolchain go1.23.6
toolchain go1.23.7
require (
github.com/blang/semver/v4 v4.0.0
github.com/cheggaaa/pb/v3 v3.1.6
github.com/cheggaaa/pb/v3 v3.1.7
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/color v1.18.0
github.com/go-openapi/errors v0.22.0
@@ -14,18 +14,18 @@ require (
github.com/go-openapi/runtime v0.28.0
github.com/go-openapi/spec v0.21.0
github.com/go-openapi/strfmt v0.23.0
github.com/go-openapi/swag v0.23.0
github.com/go-openapi/swag v0.23.1
github.com/go-openapi/validate v0.24.0
github.com/golang-jwt/jwt/v4 v4.5.1
github.com/google/uuid v1.6.0
github.com/jessevdk/go-flags v1.6.1
github.com/klauspost/compress v1.17.11
github.com/klauspost/compress v1.18.0
github.com/minio/cli v1.24.2
github.com/minio/highwayhash v1.0.3
github.com/minio/kes v0.24.0
github.com/minio/madmin-go/v3 v3.0.91
github.com/minio/mc v0.0.0-20250208210632-10c50368c526
github.com/minio/minio-go/v7 v7.0.85
github.com/minio/madmin-go/v3 v3.0.98
github.com/minio/mc v0.0.0-20250313080218-cf909e1063a9
github.com/minio/minio-go/v7 v7.0.88
github.com/minio/selfupdate v0.6.0
github.com/minio/websocket v1.6.0
github.com/mitchellh/go-homedir v1.1.0
@@ -34,16 +34,16 @@ require (
github.com/stretchr/testify v1.10.0
github.com/tidwall/gjson v1.18.0 // indirect
github.com/unrolled/secure v1.17.0
golang.org/x/crypto v0.33.0
golang.org/x/net v0.35.0
golang.org/x/oauth2 v0.26.0
golang.org/x/crypto v0.36.0
golang.org/x/net v0.37.0
golang.org/x/oauth2 v0.28.0
// Added to include security fix for
// https://github.com/golang/go/issues/56152
golang.org/x/text v0.22.0 // indirect
golang.org/x/text v0.23.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
require github.com/minio/pkg/v3 v3.0.29
require github.com/minio/pkg/v3 v3.1.2
require (
aead.dev/mem v0.2.0 // indirect
@@ -55,7 +55,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charmbracelet/bubbles v0.20.0 // indirect
github.com/charmbracelet/bubbletea v1.3.0 // indirect
github.com/charmbracelet/bubbletea v1.3.4 // indirect
github.com/charmbracelet/lipgloss v1.0.0 // indirect
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
@@ -63,14 +63,14 @@ require (
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-openapi/analysis v0.23.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
@@ -79,18 +79,18 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jedib0t/go-pretty/v6 v6.6.5 // indirect
github.com/jedib0t/go-pretty/v6 v6.6.7 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/juju/ratelimit v1.0.2 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.6 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/jwx/v2 v2.1.3 // indirect
github.com/lestrrat-go/jwx/v2 v2.1.4 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
github.com/lufia/plan9stats v0.0.0-20250303091104-876f3ea5145d // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-ieproxy v0.0.12 // indirect
@@ -99,15 +99,15 @@ require (
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/minio/colorjson v1.0.8 // indirect
github.com/minio/crc64nvme v1.0.1 // indirect
github.com/minio/filepath v1.0.0 // indirect
github.com/minio/kms-go/kes v0.3.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
@@ -116,12 +116,12 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/posener/complete v1.2.3 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_golang v1.21.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/prom2json v1.4.1 // indirect
github.com/prometheus/prometheus v0.301.0 // indirect
github.com/prometheus/prometheus v0.302.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rjeczalik/notify v0.9.3 // indirect
github.com/safchain/ethtool v0.5.10 // indirect
@@ -131,22 +131,22 @@ require (
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tinylib/msgp v1.2.5 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect
github.com/vbauerster/mpb/v8 v8.9.2 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/vbauerster/mpb/v8 v8.9.3 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.etcd.io/etcd/api/v3 v3.5.18 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.18 // indirect
go.etcd.io/etcd/client/v3 v3.5.18 // indirect
go.mongodb.org/mongo-driver v1.17.2 // indirect
go.etcd.io/etcd/api/v3 v3.5.19 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.19 // indirect
go.etcd.io/etcd/client/v3 v3.5.19 // indirect
go.mongodb.org/mongo-driver v1.17.3 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250207221924-e9438ea467c6 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect
google.golang.org/grpc v1.70.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250311190419-81fb87f6b8bf // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf // indirect
google.golang.org/grpc v1.71.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

164
go.sum
View File

@@ -22,8 +22,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
github.com/charmbracelet/bubbletea v1.3.0 h1:fPMyirm0u3Fou+flch7hlJN9krlnVURrkUVDwqXjoAc=
github.com/charmbracelet/bubbletea v1.3.0/go.mod h1:eTaHfqbIwvBhFQM/nlT1NsGc4kp8jhF8LfUK67XiTDM=
github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI=
github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo=
github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg=
github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo=
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
@@ -34,8 +34,8 @@ github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQ
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo=
github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30=
github.com/cheggaaa/pb/v3 v3.1.6 h1:h0x+vd7EiUohAJ29DJtJy+SNAc55t/elW3jCD086EXk=
github.com/cheggaaa/pb/v3 v3.1.6/go.mod h1:urxmfVtaxT+9aWk92DbsvXFZtNSWQSO5TRAp+MJ3l1s=
github.com/cheggaaa/pb/v3 v3.1.7 h1:2FsIW307kt7A/rz/ZI2lvPO+v3wKazzE4K/0LtTWsOI=
github.com/cheggaaa/pb/v3 v3.1.7/go.mod h1:/Ji89zfVPeC/u5j8ukD0MBPHt2bzTYp74lQ7KlgFWTQ=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
@@ -43,8 +43,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
@@ -69,8 +69,8 @@ github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC0
github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=
@@ -81,8 +81,8 @@ github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9Z
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
@@ -95,8 +95,8 @@ github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -109,8 +109,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jedib0t/go-pretty/v6 v6.6.5 h1:9PgMJOVBedpgYLI56jQRJYqngxYAAzfEUua+3NgSqAo=
github.com/jedib0t/go-pretty/v6 v6.6.5/go.mod h1:Uq/HrbhuFty5WSVNfjpQQe47x16RwVGXIveNGEyGtHs=
github.com/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPItz/Uuo=
github.com/jedib0t/go-pretty/v6 v6.6.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -119,11 +119,11 @@ github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -138,14 +138,14 @@ github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCG
github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/jwx/v2 v2.1.3 h1:Ud4lb2QuxRClYAmRleF50KrbKIoM1TddXgBrneT5/Jo=
github.com/lestrrat-go/jwx/v2 v2.1.3/go.mod h1:q6uFgbgZfEmQrfJfrCo90QcQOcXFMfbI/fO0NqRtvZo=
github.com/lestrrat-go/jwx/v2 v2.1.4 h1:uBCMmJX8oRZStmKuMMOFb0Yh9xmEMgNJLgjuKKt4/qc=
github.com/lestrrat-go/jwx/v2 v2.1.4/go.mod h1:nWRbDFR1ALG2Z6GJbBXzfQaYyvn751KuuyySN2yR6is=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/lufia/plan9stats v0.0.0-20250303091104-876f3ea5145d h1:fjMbDVUGsMQiVZnSQsmouYJvMdwsGiDipOZoN66v844=
github.com/lufia/plan9stats v0.0.0-20250303091104-876f3ea5145d/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -170,6 +170,8 @@ github.com/minio/cli v1.24.2 h1:J+fCUh9mhPLjN3Lj/YhklXvxj8mnyE/D6FpFduXJ2jg=
github.com/minio/cli v1.24.2/go.mod h1:bYxnK0uS629N3Bq+AOZZ+6lwF77Sodk4+UL9vNuXhOY=
github.com/minio/colorjson v1.0.8 h1:AS6gEQ1dTRYHmC4xuoodPDRILHP/9Wz5wYUGDQfPLpg=
github.com/minio/colorjson v1.0.8/go.mod h1:wrs39G/4kqNlGjwqHvPlAnXuc2tlPszo6JKdSBCLN8w=
github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY=
github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/filepath v1.0.0 h1:fvkJu1+6X+ECRA6G3+JJETj4QeAYO9sV43I79H8ubDY=
github.com/minio/filepath v1.0.0/go.mod h1:/nRZA2ldl5z6jT9/KQuvZcQlxZIMQoFFQPvEXx9T/Bw=
github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
@@ -178,18 +180,18 @@ github.com/minio/kes v0.24.0 h1:iRAmVjgZ4vQV+V7wc88kADahGl1tA/USlfQzAAr+qh8=
github.com/minio/kes v0.24.0/go.mod h1:rpgBd+s918q6zCclNLkYs//SDccXLCDv/xeM2/POzKk=
github.com/minio/kms-go/kes v0.3.1 h1:K3sPFAvFbJx33XlCTUBnQo8JRmSZyDvT6T2/MQ2iC3A=
github.com/minio/kms-go/kes v0.3.1/go.mod h1:Q9Ct0KUAuN9dH0hSVa0eva45Jg99cahbZpPxeqR9rOQ=
github.com/minio/madmin-go/v3 v3.0.91 h1:ixa64WnPNeysO77Bk0OoYP8dl1jz4FVOfJ56+3CjoOc=
github.com/minio/madmin-go/v3 v3.0.91/go.mod h1:pMLdj9OtN0CANNs5tdm6opvOlDFfj0WhbztboZAjRWE=
github.com/minio/mc v0.0.0-20250208210632-10c50368c526 h1:FzxZFUgTf21n+spBVhGAed8HCSUpAN+zfOChg49zZ64=
github.com/minio/mc v0.0.0-20250208210632-10c50368c526/go.mod h1:AgzD1Axs0TauPrFSd7M3yC75TRg9zT919d9bbxhubJ4=
github.com/minio/madmin-go/v3 v3.0.98 h1:K9gyo6Men83FgMX9ZOd/x5+cghRK9rBhf8v7s+mN2nA=
github.com/minio/madmin-go/v3 v3.0.98/go.mod h1:pMLdj9OtN0CANNs5tdm6opvOlDFfj0WhbztboZAjRWE=
github.com/minio/mc v0.0.0-20250313080218-cf909e1063a9 h1:6RyInOHKL6jz8zxcAar/h6rg/aJCxDP/uFuSNvYSuMI=
github.com/minio/mc v0.0.0-20250313080218-cf909e1063a9/go.mod h1:h5UQZ+5Qfq6XV81E4iZSgStPZ6Hy+gMuHMkLkjq4Gys=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.85 h1:9psTLS/NTvC3MWoyjhjXpwcKoNbkongaCSF3PNpSuXo=
github.com/minio/minio-go/v7 v7.0.85/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY=
github.com/minio/minio-go/v7 v7.0.88 h1:v8MoIJjwYxOkehp+eiLIuvXk87P2raUtoU5klrAAshs=
github.com/minio/minio-go/v7 v7.0.88/go.mod h1:33+O8h0tO7pCeCWwBVa07RhVVfB/3vS4kEX7rwYKmIg=
github.com/minio/mux v1.9.0 h1:dWafQFyEfGhJvK6AwLOt83bIG5bxKxKJnKMCi0XAaoA=
github.com/minio/mux v1.9.0/go.mod h1:1pAare17ZRL5GpmNL+9YmqHoWnLmMZF9C/ioUCfy0BQ=
github.com/minio/pkg/v3 v3.0.29 h1:Gcj4MndnSzPFtI8yQ5utE2/zDgtNynCHTM0EN54/2RE=
github.com/minio/pkg/v3 v3.0.29/go.mod h1:mIaN552nu0D2jiSk5BQC8LB25f44ytbOBJCuLtksX7Q=
github.com/minio/pkg/v3 v3.1.2 h1:DCIzGiUM2uJelWKP1wzwyP8KqdNXHrxMkG52S337oN8=
github.com/minio/pkg/v3 v3.1.2/go.mod h1:XIUU35+I9lWuTuMf94pwnQjvli6nZfRND6TjZGgqSEE=
github.com/minio/selfupdate v0.6.0 h1:i76PgT0K5xO9+hjzKcacQtO7+MjJ4JKA8Ak8XQ9DDwU=
github.com/minio/selfupdate v0.6.0/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM=
github.com/minio/websocket v1.6.0 h1:CPvnQvNvlVaQmvw5gtJNyYQhg4+xRmrPNhBbv8BdpAE=
@@ -198,16 +200,14 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
@@ -225,8 +225,8 @@ github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXq
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
@@ -235,8 +235,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/prom2json v1.4.1 h1:7McxdrHgPEOtMwWjkKtd0v5AhpR2Q6QAnlHKVxq0+tQ=
github.com/prometheus/prom2json v1.4.1/go.mod h1:CzOQykSKFxXuC7ELUZHOHQvwKesQ3eN0p2PWLhFitQM=
github.com/prometheus/prometheus v0.301.0 h1:0z8dgegmILivNomCd79RKvVkIols8vBGPKmcIBc7OyY=
github.com/prometheus/prometheus v0.301.0/go.mod h1:BJLjWCKNfRfjp7Q48DrAjARnCi7GhfUVvUFEAWTssZM=
github.com/prometheus/prometheus v0.302.1 h1:xqVdrwrB4WNpdgJqxsz5loqFWNUZitsK8myqLuSZ6Ag=
github.com/prometheus/prometheus v0.302.1/go.mod h1:YcyCoTbUR/TM8rY3Aoeqr0AWTu/pu1Ehh+trpX3eRzg=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
@@ -274,38 +274,38 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po=
github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/unrolled/secure v1.17.0 h1:Io7ifFgo99Bnh0J7+Q+qcMzWM6kaDPCA5FroFZEdbWU=
github.com/unrolled/secure v1.17.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/vbauerster/mpb/v8 v8.9.2 h1:kb91+D643Qg040bbICYtzpjgZ9ypVO/+sjv4Jcm6si4=
github.com/vbauerster/mpb/v8 v8.9.2/go.mod h1:hxS8Hz4C6ijnppDSIX6LjG8FYJSoPo9iIOcE53Zik0c=
github.com/vbauerster/mpb/v8 v8.9.3 h1:PnMeF+sMvYv9u23l6DO6Q3+Mdj408mjLRXIzmUmU2Z8=
github.com/vbauerster/mpb/v8 v8.9.3/go.mod h1:hxS8Hz4C6ijnppDSIX6LjG8FYJSoPo9iIOcE53Zik0c=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/etcd/api/v3 v3.5.18 h1:Q4oDAKnmwqTo5lafvB+afbgCDF7E35E4EYV2g+FNGhs=
go.etcd.io/etcd/api/v3 v3.5.18/go.mod h1:uY03Ob2H50077J7Qq0DeehjM/A9S8PhVfbQ1mSaMopU=
go.etcd.io/etcd/client/pkg/v3 v3.5.18 h1:mZPOYw4h8rTk7TeJ5+3udUkfVGBqc+GCjOJYd68QgNM=
go.etcd.io/etcd/client/pkg/v3 v3.5.18/go.mod h1:BxVf2o5wXG9ZJV+/Cu7QNUiJYk4A29sAhoI5tIRsCu4=
go.etcd.io/etcd/client/v3 v3.5.18 h1:nvvYmNHGumkDjZhTHgVU36A9pykGa2K4lAJ0yY7hcXA=
go.etcd.io/etcd/client/v3 v3.5.18/go.mod h1:kmemwOsPU9broExyhYsBxX4spCTDX3yLgPMWtpBXG6E=
go.mongodb.org/mongo-driver v1.17.2 h1:gvZyk8352qSfzyZ2UMWcpDpMSGEr1eqE4T793SqyhzM=
go.mongodb.org/mongo-driver v1.17.2/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.etcd.io/etcd/api/v3 v3.5.19 h1:w3L6sQZGsWPuBxRQ4m6pPP3bVUtV8rjW033EGwlr0jw=
go.etcd.io/etcd/api/v3 v3.5.19/go.mod h1:QqKGViq4KTgOG43dr/uH0vmGWIaoJY3ggFi6ZH0TH/U=
go.etcd.io/etcd/client/pkg/v3 v3.5.19 h1:9VsyGhg0WQGjDWWlDI4VuaS9PZJGNbPkaHEIuLwtixk=
go.etcd.io/etcd/client/pkg/v3 v3.5.19/go.mod h1:qaOi1k4ZA9lVLejXNvyPABrVEe7VymMF2433yyRQ7O0=
go.etcd.io/etcd/client/v3 v3.5.19 h1:+4byIz6ti3QC28W0zB0cEZWwhpVHXdrKovyycJh1KNo=
go.etcd.io/etcd/client/v3 v3.5.19/go.mod h1:FNzyinmMIl0oVsty1zA3hFeUrxXI/JpEnz4sG+POzjU=
go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -318,8 +318,8 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -327,16 +327,16 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -356,17 +356,17 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@@ -375,12 +375,12 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/api v0.0.0-20250207221924-e9438ea467c6 h1:L9JNMl/plZH9wmzQUHleO/ZZDSN+9Gh41wPczNy+5Fk=
google.golang.org/genproto/googleapis/api v0.0.0-20250207221924-e9438ea467c6/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/genproto/googleapis/api v0.0.0-20250311190419-81fb87f6b8bf h1:BdIVRm+fyDUn8lrZLPSlBCfM/YKDwUBYgDoLv9+DYo0=
google.golang.org/genproto/googleapis/api v0.0.0-20250311190419-81fb87f6b8bf/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf h1:dHDlF3CWxQkefK9IJx+O8ldY0gLygvrlYRBNbPqDWuY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250311190419-81fb87f6b8bf/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -59,6 +59,7 @@
"proxy": "http://localhost:9090/",
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/runtime": "^7.26.10",
"@playwright/test": "^1.50.1",
"@types/jest": "^29.5.14",
"@types/lodash": "^4.17.15",
@@ -95,7 +96,8 @@
"ws": "^8.17.1",
"rollup": "^4.24.0",
"cookie": "^0.7.2",
"jspdf": "^3.0.0"
"jspdf": "^3.0.0",
"@babel/runtime": "^7.26.10"
},
"main": "index.js",
"packageManager": "yarn@4.4.0"

View File

@@ -1652,12 +1652,12 @@ __metadata:
languageName: node
linkType: hard
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
version: 7.26.0
resolution: "@babel/runtime@npm:7.26.0"
"@babel/runtime@npm:^7.26.10":
version: 7.26.10
resolution: "@babel/runtime@npm:7.26.10"
dependencies:
regenerator-runtime: "npm:^0.14.0"
checksum: 10c0/12c01357e0345f89f4f7e8c0e81921f2a3e3e101f06e8eaa18a382b517376520cd2fa8c237726eb094dab25532855df28a7baaf1c26342b52782f6936b07c287
checksum: 10c0/6dc6d88c7908f505c4f7770fb4677dfa61f68f659b943c2be1f2a99cb6680343462867abf2d49822adc435932919b36c77ac60125793e719ea8745f2073d3745
languageName: node
linkType: hard
@@ -17914,6 +17914,7 @@ __metadata:
resolution: "web-app@workspace:."
dependencies:
"@babel/plugin-proposal-private-property-in-object": "npm:^7.21.11"
"@babel/runtime": "npm:^7.26.10"
"@playwright/test": "npm:^1.50.1"
"@reduxjs/toolkit": "npm:^1.9.7"
"@types/jest": "npm:^29.5.14"