mirror of
https://github.com/OpenMaxIO/openmaxio-object-browser
synced 2026-07-01 07:41:18 -07:00
Implemented Log Search API & Prometheus functionality (#549)
Implemented Log Search API & Prometheus functionality in console, also fixed minor issues in all the platform Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -4,3 +4,4 @@ target/
|
||||
console
|
||||
!console/
|
||||
portal-ui/node_modules/
|
||||
.git/
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -24,6 +24,11 @@ console
|
||||
|
||||
dist/
|
||||
|
||||
# Ignore node_modules
|
||||
|
||||
portal-ui/node_modules/
|
||||
portal-ui/build/
|
||||
|
||||
# Ignore tls cert and key
|
||||
private.key
|
||||
public.crt
|
||||
|
||||
@@ -33,7 +33,7 @@ RUN go mod download
|
||||
ADD . /go/src/github.com/minio/console/
|
||||
WORKDIR /go/src/github.com/minio/console/
|
||||
|
||||
COPY --from=uilayer /app/bindata_assetfs.go /go/src/github.com/minio/console/portal-ui/
|
||||
COPY --from=uilayer /app/bindata_assetfs.go /go/src/github.com/minio/console/portal-ui/bindata_assetfs.go
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import (
|
||||
|
||||
var (
|
||||
errCantDetermineMinIOImage = errors.New("can't determine MinIO Image")
|
||||
errCantDetermineMCImage = errors.New("can't determine MC Image")
|
||||
)
|
||||
|
||||
func GetK8sAPIServer() string {
|
||||
@@ -120,44 +119,3 @@ func GetLatestMinioImage(client HTTPClientI) (*string, error) {
|
||||
}
|
||||
return latestMinIOImage, nil
|
||||
}
|
||||
|
||||
// getLatestMCImage returns the latest docker image for MC if found on the internet
|
||||
func getLatestMCImage() (*string, error) {
|
||||
// Create an http client with a 4 second timeout
|
||||
client := http.Client{
|
||||
Timeout: 4 * time.Second,
|
||||
}
|
||||
resp, err := client.Get("https://dl.min.io/client/mc/release/linux-amd64/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var re = regexp.MustCompile(`(?m)\.\/mc\.(RELEASE.*?Z)"`)
|
||||
// look for a single match
|
||||
matches := re.FindAllStringSubmatch(string(body), 1)
|
||||
for i := range matches {
|
||||
release := matches[i][1]
|
||||
dockerImage := fmt.Sprintf("minio/mc:%s", release)
|
||||
return &dockerImage, nil
|
||||
}
|
||||
return nil, errCantDetermineMCImage
|
||||
}
|
||||
|
||||
var latestMCImage, errLatestMCImage = getLatestMCImage()
|
||||
|
||||
func GetMCImage() (*string, error) {
|
||||
image := strings.TrimSpace(env.Get(ConsoleMCImage, ""))
|
||||
// if there is a preferred image configured by the user we'll always return that
|
||||
if image != "" {
|
||||
return &image, nil
|
||||
}
|
||||
if errLatestMCImage != nil {
|
||||
return nil, errLatestMCImage
|
||||
}
|
||||
return latestMCImage, nil
|
||||
}
|
||||
|
||||
5
go.mod
5
go.mod
@@ -20,6 +20,7 @@ require (
|
||||
github.com/minio/minio v0.0.0-20201221162327-6df6ac0f3410
|
||||
github.com/minio/minio-go/v7 v7.0.7-0.20201217170524-3baf9ea06f7c
|
||||
github.com/minio/operator v0.0.0-20201204220226-9901d1d0766c
|
||||
github.com/minio/operator/logsearchapi v0.0.0-20201217190212-bf6546b09012
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/secure-io/sio-go v0.3.1
|
||||
@@ -30,6 +31,8 @@ require (
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
k8s.io/api v0.18.6
|
||||
k8s.io/apimachinery v0.18.6
|
||||
k8s.io/apimachinery v0.18.8
|
||||
k8s.io/client-go v0.18.6
|
||||
)
|
||||
|
||||
replace github.com/minio/operator v0.0.0-20201204220226-9901d1d0766c => github.com/dvaldivia/operator v0.0.0-20201230052356-04efc0ea5890
|
||||
|
||||
159
go.sum
159
go.sum
@@ -123,6 +123,7 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUW
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
|
||||
github.com/alecthomas/participle v0.2.1 h1:4AVLj1viSGa4LG5HDXKXrm5xRx19SB/rS/skPQB1Grw=
|
||||
github.com/alecthomas/participle v0.2.1/go.mod h1:SW6HZGeZgSIpcUWX3fXpfZhuaWHnmoD5KCVaqSaNTkk=
|
||||
@@ -131,6 +132,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/alessio/shellescape v1.2.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ=
|
||||
@@ -165,6 +168,7 @@ github.com/aws/aws-sdk-go v1.31.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU
|
||||
github.com/aws/aws-sdk-go v1.35.20/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
||||
github.com/bcicen/jstream v0.0.0-20190220045926-16c1f8af81c2/go.mod h1:RDu/qcrnpEdJC/p8tx34+YBFqqX71lB7dOX9QE+ZC4M=
|
||||
github.com/bcicen/jstream v1.0.1 h1:BXY7Cu4rdmc0rhyTVyT3UkxAiX3bnLpKLas9btbH5ck=
|
||||
github.com/bcicen/jstream v1.0.1/go.mod h1:9ielPxqFry7Y4Tg3j4BfjPocfJ3TbsRtXOAYXYmRuAQ=
|
||||
github.com/beevik/ntp v0.3.0 h1:xzVrPrE4ziasFXgBVBZJDP0Wg/KpMwk2KHJ4Ba8GrDw=
|
||||
@@ -194,6 +198,7 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheggaaa/pb v1.0.28/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||
github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo=
|
||||
github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
@@ -204,6 +209,10 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.0.3 h1:ZA346ACHIZctef6trOTwBAEvPVm1k0uLm/bb2Atc+S8=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.0.3/go.mod h1:hAuDgiVgDVkfirP9JnhXEfcXEPRKBpYdGz+l7mvYSzw=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
@@ -227,6 +236,9 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
@@ -242,6 +254,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4=
|
||||
github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
@@ -268,6 +281,8 @@ github.com/dswarbrick/smart v0.0.0-20190505152634-909a45200d6d/go.mod h1:apXo4PA
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dvaldivia/operator v0.0.0-20201230052356-04efc0ea5890 h1:vfbzHXeky4e8EOS40YPSLNCxNi7KkevPB7hi5tu/O9g=
|
||||
github.com/dvaldivia/operator v0.0.0-20201230052356-04efc0ea5890/go.mod h1:EmTYFyr2nk12P9afUXPPsCKbNhYDruQ/PuZtDenESH0=
|
||||
github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q=
|
||||
@@ -291,8 +306,12 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/etcd-io/gofail v0.0.0-20190801230047-ad7f989257ca/go.mod h1:49H/RkXP8pKaZy4h0d+NW16rSLhyVBt4o6VLJbmOqDE=
|
||||
github.com/evanphx/json-patch v0.0.0-20200808040245-162e5629780b/go.mod h1:NAJj0yf/KaRKURN6nyi7A9IZydMivZEm9oQLWNjfKDc=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch/v5 v5.1.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
@@ -312,6 +331,8 @@ github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/georgysavva/scany v0.2.7 h1:SBEuurTvWOUp7FnGBOjeSF9XWaWmVzc91h9baPo6y2s=
|
||||
github.com/georgysavva/scany v0.2.7/go.mod h1:bcxPhzeQFQqAUmjlZVwTGlu6AnWFSOiHpalfBe0xQ6U=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
@@ -367,6 +388,7 @@ github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf
|
||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
|
||||
github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI=
|
||||
github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
|
||||
github.com/go-openapi/loads v0.19.5 h1:jZVYWawIQiA1NBnHla28ktg6hrcfTHsCE+3QLVRBIls=
|
||||
github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY=
|
||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
||||
@@ -402,6 +424,7 @@ github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfT
|
||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||
github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=
|
||||
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
||||
github.com/go-openapi/validate v0.19.10 h1:tG3SZ5DC5KF4cyt7nqLVcQXGj5A7mpaYkAcNPlDK+Yk=
|
||||
github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
@@ -434,6 +457,7 @@ github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSC
|
||||
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
|
||||
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
|
||||
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
|
||||
github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
|
||||
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
|
||||
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
|
||||
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
|
||||
@@ -451,7 +475,10 @@ github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGt
|
||||
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
|
||||
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
@@ -459,6 +486,7 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -676,6 +704,74 @@ github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7V
|
||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
|
||||
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||
github.com/jackc/pgconn v1.7.0 h1:pwjzcYyfmz/HQOQlENvG1OcDqauTGaqlVahq934F0/U=
|
||||
github.com/jackc/pgconn v1.7.0/go.mod h1:sF/lPpNEMEOp+IYhyQGdAvrG20gWf6A1tKlr0v7JMeA=
|
||||
github.com/jackc/pgconn v1.8.0 h1:FmjZ0rOyXTr1wfWs45i4a9vjnjWUAGpMuQLD9OSs+lw=
|
||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.5 h1:NUbEWPmCQZbMmYlTjVoNPhc0CfnYyz2bfUAh6A5ZVJM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.5/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6 h1:b1105ZGEMFe7aCvrT1Cca3VoVb4ZFMaFJLJcg/3zD+8=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||
github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
|
||||
github.com/jackc/pgtype v1.3.0/go.mod h1:b0JqxHvPmljG+HQ5IsvQ0yqeSi4nGcDTVjFoiLDb0Ik=
|
||||
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
|
||||
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
|
||||
github.com/jackc/pgtype v1.3.1-0.20200612023650-09efc3839047/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
|
||||
github.com/jackc/pgtype v1.5.0 h1:jzBqRk2HFG2CV4AIwgCI2PwTgm6UUoCAK2ofHHRirtc=
|
||||
github.com/jackc/pgtype v1.5.0/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
|
||||
github.com/jackc/pgtype v1.6.2 h1:b3pDeuhbbzBYcg5kwNmNDun4pFUD/0AAr1kLXZLeNt8=
|
||||
github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
|
||||
github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o=
|
||||
github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
|
||||
github.com/jackc/pgx/v4 v4.6.0/go.mod h1:vPh43ZzxijXUVJ+t/EmXBtFmbFVO72cuneCT9oAlxAg=
|
||||
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
|
||||
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
|
||||
github.com/jackc/pgx/v4 v4.9.0 h1:6STjDqppM2ROy5p1wNDcsC7zJTjSHeuCsguZmXyzx7c=
|
||||
github.com/jackc/pgx/v4 v4.9.0/go.mod h1:MNGWmViCgqbZck9ujOOBN63gK9XVGILXWCvKLGKmnms=
|
||||
github.com/jackc/pgx/v4 v4.10.0 h1:xXTl+lSiF1eFQ4U7vUL493n/1q8ZhSDP962rSKhgRZo=
|
||||
github.com/jackc/pgx/v4 v4.10.0/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.2 h1:mpQEXihFnWGDy6X98EOTh81JYuxn7txby8ilJ3iIPGM=
|
||||
github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||
github.com/jcmturner/gofork v0.0.0-20180107083740-2aebee971930/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
@@ -684,6 +780,9 @@ github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/U
|
||||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
|
||||
github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
@@ -691,6 +790,7 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
@@ -724,6 +824,7 @@ github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0
|
||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc=
|
||||
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
@@ -734,6 +835,7 @@ github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo
|
||||
github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
|
||||
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
|
||||
github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/klauspost/readahead v1.3.1 h1:QqXNYvm+VvqYcbrRT4LojUciM0XrznFRIDrbHiJtu/0=
|
||||
@@ -756,8 +858,11 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.4.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
|
||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
@@ -795,7 +900,9 @@ github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
@@ -806,6 +913,7 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
@@ -825,16 +933,20 @@ github.com/minio/mc v0.0.0-20201220181029-41c804b179de h1:bm1FlYN5TSkB9/IpqREPkB
|
||||
github.com/minio/mc v0.0.0-20201220181029-41c804b179de/go.mod h1:2nbeDAB4Bkges8QHq6yKoJnjHz3lYqUeHaLGFDUDUbY=
|
||||
github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=
|
||||
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
|
||||
github.com/minio/minio v0.0.0-20201029200030-7331659d3ddc/go.mod h1:640kMkCwiyOX8dheptHYModdUw4HrnUFHKw3McqUXD8=
|
||||
github.com/minio/minio v0.0.0-20201203193910-919441d9c4d2/go.mod h1:+wH6R6AjgNDUvMBb24p/e7zn8VU+ChUAXy4uhopGxQA=
|
||||
github.com/minio/minio v0.0.0-20201219173637-3e16ec457a24/go.mod h1:sIgZV5KcIpn3bitFPXbrx/OyGcca57QzrgxT5I8nabg=
|
||||
github.com/minio/minio v0.0.0-20201221162327-6df6ac0f3410 h1:2mM//tAaamqL1SmqYVb5rour6czx+mWhchs12goYT/w=
|
||||
github.com/minio/minio v0.0.0-20201221162327-6df6ac0f3410/go.mod h1:sIgZV5KcIpn3bitFPXbrx/OyGcca57QzrgxT5I8nabg=
|
||||
github.com/minio/minio-go/v7 v7.0.6-0.20200929220449-755b5633803a/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk=
|
||||
github.com/minio/minio-go/v7 v7.0.6 h1:9czXaG0LEZ9s74smSqy0rm034MxngQoP6HTTuSc5GEs=
|
||||
github.com/minio/minio-go/v7 v7.0.6/go.mod h1:HcIuq+11d/3MfavIPZiswSzfQ1VJ2Lwxp/XLtW46IWQ=
|
||||
github.com/minio/minio-go/v7 v7.0.7-0.20201217170524-3baf9ea06f7c h1:NgTbI1w/B+2Jcl+YKTULAAXqkwWqMZbkzmVdWNwzKnA=
|
||||
github.com/minio/minio-go/v7 v7.0.7-0.20201217170524-3baf9ea06f7c/go.mod h1:pEZBUa+L2m9oECoIA6IcSK8bv/qggtQVLovjeKK5jYc=
|
||||
github.com/minio/operator v0.0.0-20201204220226-9901d1d0766c h1:2QpnenH2gieq5yVh6sZYylXKCoBgwKkxcgqkLr/fq9M=
|
||||
github.com/minio/operator v0.0.0-20201204220226-9901d1d0766c/go.mod h1:Xnb44PIBZF/JCN4uXEEzf9vFwhnB9zXsQgVKU7GThiM=
|
||||
github.com/minio/operator/logsearchapi v0.0.0-20201217190212-bf6546b09012 h1:UnFJL5tYkdtXPeWUoxGwDkpVoWRwT4Fs1SFmjbcNjls=
|
||||
github.com/minio/operator/logsearchapi v0.0.0-20201217190212-bf6546b09012/go.mod h1:kpA0C8LRbfUGVzGSC+Px7WYRiczblwqiRnP0ENv0tCU=
|
||||
github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=
|
||||
github.com/minio/selfupdate v0.3.1/go.mod h1:b8ThJzzH7u2MkF6PcIra7KaXO9Khf6alWPvMSyTDCFM=
|
||||
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
|
||||
@@ -961,6 +1073,7 @@ github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bA
|
||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
|
||||
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
@@ -1045,6 +1158,8 @@ github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@@ -1067,10 +1182,15 @@ github.com/securego/gosec v0.0.0-20200401082031-e946c8c39989/go.mod h1:i9l/TNj+y
|
||||
github.com/securego/gosec/v2 v2.3.0/go.mod h1:UzeVyUXbxukhLeHKV3VVqo7HdoQR9MrRfFmZYotn8ME=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
|
||||
github.com/shirou/gopsutil v2.20.3-0.20200314133625-53cec6b37e6a+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil v2.20.9+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil v3.20.11+incompatible h1:LJr4ZQK4mPpIV5gOa4jCOKOGb4ty4DZO54I4FGqIpto=
|
||||
github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v0.0.0-20200419222939-1884f454f8ea h1:jaXWVFZ98/ihXniiDzqNXQgMSgklX4kjfDWZTE3ZtdU=
|
||||
github.com/shopspring/decimal v0.0.0-20200419222939-1884f454f8ea/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
@@ -1127,6 +1247,7 @@ github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1Sd
|
||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
@@ -1150,6 +1271,7 @@ github.com/tidwall/sjson v1.0.4 h1:UcdIRXff12Lpnu3OLtZvnc03g4vH2suXDXhBwBqmzYg=
|
||||
github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y=
|
||||
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||
github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tinylib/msgp v1.1.3 h1:3giwAkmtaEDLSV0MdO1lDLuPgklgPzmk8H9+So2BVfA=
|
||||
github.com/tinylib/msgp v1.1.3/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
||||
@@ -1179,6 +1301,7 @@ github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOV
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/vdemeester/k8s-pkg-credentialprovider v1.17.4/go.mod h1:inCTmtUdr5KJbreVojo06krnTgaeAz/Z7lynpPk/Q2c=
|
||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||
github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
||||
github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE=
|
||||
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
||||
@@ -1199,6 +1322,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||
@@ -1207,8 +1331,10 @@ go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b h1:5makfKENOTVu2bNoHzSqwwz+g70ivWLSnExzd33/2bI=
|
||||
go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
|
||||
go.etcd.io/etcd/v3 v3.3.0-rc.0.0.20200707003333-58bb8ae09f8e/go.mod h1:UENlOa05tkNvLx9VnNziSerG4Ro74upGK6Apd4v6M/Y=
|
||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
|
||||
go.mongodb.org/mongo-driver v1.3.4 h1:zs/dKNwX0gYUtzwrN9lLiR15hCO0nDwQj5xXx+vjCdE=
|
||||
go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
|
||||
@@ -1232,6 +1358,7 @@ go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo=
|
||||
@@ -1245,7 +1372,9 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -1255,9 +1384,12 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -1400,6 +1532,7 @@ golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1428,6 +1561,7 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200915084602-288bc346aa39/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1458,6 +1592,7 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190221204921-83362c3779f5/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -1473,6 +1608,7 @@ golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
@@ -1485,6 +1621,7 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -1525,9 +1662,12 @@ golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200616195046-dc31b401abb5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f h1:7+Nz9MyPqt2qMCTvNiRy1G0zYfkB7UCa+ayT6uVvbyI=
|
||||
golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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=
|
||||
@@ -1640,6 +1780,7 @@ gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/h2non/filetype.v1 v1.0.5 h1:CC1jjJjoEhNVbMhXYalmGBhOBK2V70Q1N850wt/98/Y=
|
||||
gopkg.in/h2non/filetype.v1 v1.0.5/go.mod h1:M0yem4rwSX5lLVrkEuRRp2/NinFMD5vgJ4DlAhZcfNo=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
@@ -1675,12 +1816,14 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
@@ -1696,28 +1839,41 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
||||
honnef.co/go/tools v0.0.1-2020.1.5 h1:nI5egYTGJakVyOryqLs1cQO5dO0ksin5XXs2pspk75k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA=
|
||||
k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
|
||||
k8s.io/api v0.18.6 h1:osqrAXbOQjkKIWDTjrqxWQ3w0GkKb1KA1XkUGHHYpeE=
|
||||
k8s.io/api v0.18.6/go.mod h1:eeyxr+cwCjMdLAmr2W3RyDI0VvTawSg/3RFFBEnmZGI=
|
||||
k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
|
||||
k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
|
||||
k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
|
||||
k8s.io/apimachinery v0.18.6 h1:RtFHnfGNfd1N0LeSrKCUznz5xtUP1elRGvHJbL3Ntag=
|
||||
k8s.io/apimachinery v0.18.6/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
|
||||
k8s.io/apimachinery v0.18.8 h1:jimPrycCqgx2QPearX3to1JePz7wSbVLq+7PdBTTwQ0=
|
||||
k8s.io/apimachinery v0.18.8/go.mod h1:6sQd+iHEqmOtALqOFjSWp2KZ9F0wlU/nWm0ZgsYWMig=
|
||||
k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I=
|
||||
k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
|
||||
k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc=
|
||||
k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
|
||||
k8s.io/client-go v0.18.6 h1:I+oWqJbibLSGsZj8Xs8F0aWVXJVIoUHWaaJV3kUN/Zw=
|
||||
k8s.io/client-go v0.18.6/go.mod h1:/fwtGLjYMS1MaM5oi+eXhKwG+1UHidUEXRh6cNsdO0Q=
|
||||
k8s.io/cloud-provider v0.17.4/go.mod h1:XEjKDzfD+b9MTLXQFlDGkk6Ho8SGMpaU8Uugx/KNK9U=
|
||||
k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
|
||||
k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
|
||||
k8s.io/component-base v0.17.4/go.mod h1:5BRqHMbbQPm2kKu35v3G+CpVq4K0RJKC7TRioF0I9lE=
|
||||
k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM=
|
||||
k8s.io/csi-translation-lib v0.17.4/go.mod h1:CsxmjwxEI0tTNMzffIAcgR9lX4wOh6AKHdxQrT7L0oo=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/klog/v2 v2.3.0 h1:WmkrnW7fdrm0/DMClc+HIxtftvxVIPAhlVwMQo5yLco=
|
||||
k8s.io/klog/v2 v2.3.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
|
||||
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY=
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
k8s.io/legacy-cloud-providers v0.17.4/go.mod h1:FikRNoD64ECjkxO36gkDgJeiQWwyZTuBkhu+yxOc1Js=
|
||||
@@ -1738,6 +1894,9 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
|
||||
sigs.k8s.io/controller-tools v0.4.1/go.mod h1:G9rHdZMVlBDocIxGkK3jHLWqcTMNvveypYJwrvYKjWU=
|
||||
sigs.k8s.io/kind v0.9.0/go.mod h1:cxKQWwmbtRDzQ+RNKnR6gZG6fjbeTtItp5cGf+ww+1Y=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
|
||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
|
||||
|
||||
@@ -23,6 +23,9 @@ package models
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
@@ -40,10 +43,47 @@ type AdminInfoResponse struct {
|
||||
|
||||
// usage
|
||||
Usage int64 `json:"usage,omitempty"`
|
||||
|
||||
// widgets
|
||||
Widgets []*Widget `json:"widgets"`
|
||||
}
|
||||
|
||||
// Validate validates this admin info response
|
||||
func (m *AdminInfoResponse) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateWidgets(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AdminInfoResponse) validateWidgets(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Widgets) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Widgets); i++ {
|
||||
if swag.IsZero(m.Widgets[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Widgets[i] != nil {
|
||||
if err := m.Widgets[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("widgets" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,12 @@ type CreateTenantRequest struct {
|
||||
// erasure coding parity
|
||||
ErasureCodingParity int64 `json:"erasureCodingParity,omitempty"`
|
||||
|
||||
// expose console
|
||||
ExposeConsole bool `json:"expose_console,omitempty"`
|
||||
|
||||
// expose minio
|
||||
ExposeMinio bool `json:"expose_minio,omitempty"`
|
||||
|
||||
// idp
|
||||
Idp *IdpConfiguration `json:"idp,omitempty"`
|
||||
|
||||
|
||||
60
models/log_search_response.go
Normal file
60
models/log_search_response.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// LogSearchResponse log search response
|
||||
//
|
||||
// swagger:model logSearchResponse
|
||||
type LogSearchResponse struct {
|
||||
|
||||
// list of log search responses
|
||||
Results interface{} `json:"results,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this log search response
|
||||
func (m *LogSearchResponse) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *LogSearchResponse) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *LogSearchResponse) UnmarshalBinary(b []byte) error {
|
||||
var res LogSearchResponse
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
103
models/result_target.go
Normal file
103
models/result_target.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// ResultTarget result target
|
||||
//
|
||||
// swagger:model resultTarget
|
||||
type ResultTarget struct {
|
||||
|
||||
// legend format
|
||||
LegendFormat string `json:"legendFormat,omitempty"`
|
||||
|
||||
// result
|
||||
Result []*WidgetResult `json:"result"`
|
||||
|
||||
// result type
|
||||
ResultType string `json:"resultType,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this result target
|
||||
func (m *ResultTarget) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateResult(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ResultTarget) validateResult(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Result) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Result); i++ {
|
||||
if swag.IsZero(m.Result[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Result[i] != nil {
|
||||
if err := m.Result[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("result" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *ResultTarget) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *ResultTarget) UnmarshalBinary(b []byte) error {
|
||||
var res ResultTarget
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -50,6 +50,9 @@ type Tenant struct {
|
||||
// enable prometheus
|
||||
EnablePrometheus bool `json:"enable_prometheus,omitempty"`
|
||||
|
||||
// endpoints
|
||||
Endpoints *TenantEndpoints `json:"endpoints,omitempty"`
|
||||
|
||||
// image
|
||||
Image string `json:"image,omitempty"`
|
||||
|
||||
@@ -73,6 +76,10 @@ type Tenant struct {
|
||||
func (m *Tenant) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateEndpoints(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validatePools(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
@@ -87,6 +94,24 @@ func (m *Tenant) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Tenant) validateEndpoints(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Endpoints) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Endpoints != nil {
|
||||
if err := m.Endpoints.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("endpoints")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Tenant) validatePools(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Pools) { // not required
|
||||
@@ -147,3 +172,38 @@ func (m *Tenant) UnmarshalBinary(b []byte) error {
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
|
||||
// TenantEndpoints tenant endpoints
|
||||
//
|
||||
// swagger:model TenantEndpoints
|
||||
type TenantEndpoints struct {
|
||||
|
||||
// console
|
||||
Console string `json:"console,omitempty"`
|
||||
|
||||
// minio
|
||||
Minio string `json:"minio,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this tenant endpoints
|
||||
func (m *TenantEndpoints) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *TenantEndpoints) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *TenantEndpoints) UnmarshalBinary(b []byte) error {
|
||||
var res TenantEndpoints
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
|
||||
219
models/widget.go
Normal file
219
models/widget.go
Normal file
@@ -0,0 +1,219 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// Widget widget
|
||||
//
|
||||
// swagger:model widget
|
||||
type Widget struct {
|
||||
|
||||
// options
|
||||
Options *WidgetOptions `json:"options,omitempty"`
|
||||
|
||||
// targets
|
||||
Targets []*ResultTarget `json:"targets"`
|
||||
|
||||
// title
|
||||
Title string `json:"title,omitempty"`
|
||||
|
||||
// type
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this widget
|
||||
func (m *Widget) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateOptions(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateTargets(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Widget) validateOptions(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Options) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Options != nil {
|
||||
if err := m.Options.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("options")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Widget) validateTargets(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Targets) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Targets); i++ {
|
||||
if swag.IsZero(m.Targets[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Targets[i] != nil {
|
||||
if err := m.Targets[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("targets" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *Widget) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *Widget) UnmarshalBinary(b []byte) error {
|
||||
var res Widget
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
|
||||
// WidgetOptions widget options
|
||||
//
|
||||
// swagger:model WidgetOptions
|
||||
type WidgetOptions struct {
|
||||
|
||||
// reduce options
|
||||
ReduceOptions *WidgetOptionsReduceOptions `json:"reduceOptions,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this widget options
|
||||
func (m *WidgetOptions) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateReduceOptions(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *WidgetOptions) validateReduceOptions(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.ReduceOptions) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.ReduceOptions != nil {
|
||||
if err := m.ReduceOptions.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("options" + "." + "reduceOptions")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *WidgetOptions) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *WidgetOptions) UnmarshalBinary(b []byte) error {
|
||||
var res WidgetOptions
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
|
||||
// WidgetOptionsReduceOptions widget options reduce options
|
||||
//
|
||||
// swagger:model WidgetOptionsReduceOptions
|
||||
type WidgetOptionsReduceOptions struct {
|
||||
|
||||
// calcs
|
||||
Calcs []string `json:"calcs"`
|
||||
}
|
||||
|
||||
// Validate validates this widget options reduce options
|
||||
func (m *WidgetOptionsReduceOptions) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *WidgetOptionsReduceOptions) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *WidgetOptionsReduceOptions) UnmarshalBinary(b []byte) error {
|
||||
var res WidgetOptionsReduceOptions
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
63
models/widget_result.go
Normal file
63
models/widget_result.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// WidgetResult widget result
|
||||
//
|
||||
// swagger:model widgetResult
|
||||
type WidgetResult struct {
|
||||
|
||||
// metric
|
||||
Metric map[string]string `json:"metric,omitempty"`
|
||||
|
||||
// values
|
||||
Values []interface{} `json:"values"`
|
||||
}
|
||||
|
||||
// Validate validates this widget result
|
||||
func (m *WidgetResult) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *WidgetResult) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *WidgetResult) UnmarshalBinary(b []byte) error {
|
||||
var res WidgetResult
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -3,9 +3,11 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@date-io/moment": "1.x",
|
||||
"@hot-loader/react-dom": "17.0.1",
|
||||
"@material-ui/core": "^4.9.12",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/pickers": "^3.2.10",
|
||||
"@types/history": "^4.7.3",
|
||||
"@types/jest": "24.0.23",
|
||||
"@types/lodash": "^4.14.149",
|
||||
@@ -13,6 +15,7 @@
|
||||
"@types/react": "17.0.0",
|
||||
"@types/react-copy-to-clipboard": "^4.3.0",
|
||||
"@types/react-dom": "16.9.4",
|
||||
"@types/react-grid-layout": "^1.1.1",
|
||||
"@types/react-redux": "^7.1.5",
|
||||
"@types/react-router": "^5.1.3",
|
||||
"@types/react-router-dom": "^5.1.2",
|
||||
@@ -27,7 +30,7 @@
|
||||
"history": "^4.10.1",
|
||||
"local-storage-fallback": "^4.1.1",
|
||||
"lodash": "^4.17.19",
|
||||
"moment": "^2.24.0",
|
||||
"moment": "^2.29.1",
|
||||
"react": "17.0.1",
|
||||
"react-app-rewire-hot-loader": "^2.0.1",
|
||||
"react-app-rewired": "^2.1.6",
|
||||
@@ -36,6 +39,7 @@
|
||||
"react-codemirror2": "^7.1.0",
|
||||
"react-copy-to-clipboard": "^5.0.2",
|
||||
"react-dom": "17.0.1",
|
||||
"react-grid-layout": "^1.2.0",
|
||||
"react-hot-loader": "^4.13.0",
|
||||
"react-moment": "^0.9.7",
|
||||
"react-redux": "^7.1.3",
|
||||
|
||||
@@ -58,6 +58,8 @@ export interface ITenantCreator {
|
||||
secret_key: string;
|
||||
image: string;
|
||||
console_image: string;
|
||||
expose_minio: boolean;
|
||||
expose_console: boolean;
|
||||
pools: IPoolModel[];
|
||||
namespace: string;
|
||||
erasureCodingParity: number;
|
||||
|
||||
@@ -39,7 +39,7 @@ export const units = [
|
||||
export const k8sUnits = ["Ki", "Mi", "Gi", "Ti", "Pi", "Ei"];
|
||||
export const k8sCalcUnits = ["B", ...k8sUnits];
|
||||
|
||||
export const niceBytes = (x: string) => {
|
||||
export const niceBytes = (x: string, showK8sUnits: boolean = false) => {
|
||||
let l = 0,
|
||||
n = parseInt(x, 10) || 0;
|
||||
|
||||
@@ -48,7 +48,12 @@ export const niceBytes = (x: string) => {
|
||||
}
|
||||
//include a decimal point and a tenths-place digit if presenting
|
||||
//less than ten of KB or greater units
|
||||
return n.toFixed(n < 10 && l > 0 ? 1 : 0) + " " + units[l];
|
||||
const k8sUnitsN = ["B", ...k8sUnits];
|
||||
return (
|
||||
n.toFixed(n < 10 && l > 0 ? 1 : 0) +
|
||||
" " +
|
||||
(showK8sUnits ? k8sUnitsN[l] : units[l])
|
||||
);
|
||||
};
|
||||
|
||||
export const setCookie = (name: string, val: string) => {
|
||||
@@ -419,3 +424,122 @@ export const generatePoolName = (pools: IPoolModel[]) => {
|
||||
|
||||
return `pool-${poolCounter}`;
|
||||
};
|
||||
|
||||
// seconds / minutes /hours / Days / Years calculator
|
||||
export const niceDays = (secondsValue: string) => {
|
||||
let seconds = parseFloat(secondsValue);
|
||||
|
||||
const days = Math.floor(seconds / (3600 * 24));
|
||||
|
||||
seconds -= days * 3600 * 24;
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
seconds -= hours * 3600;
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
seconds -= minutes * 60;
|
||||
|
||||
if (days > 365) {
|
||||
const years = days / 365;
|
||||
return `${years} year${Math.floor(years) === 1 ? "" : "s"}`;
|
||||
}
|
||||
|
||||
if (days > 30) {
|
||||
const months = Math.floor(days / 30);
|
||||
const diffDays = days - months * 30;
|
||||
|
||||
return `${months} month${Math.floor(months) === 1 ? "" : "s"} ${
|
||||
diffDays > 0 ? `${diffDays} day${diffDays > 1 ? "s" : ""}` : ""
|
||||
}`;
|
||||
}
|
||||
|
||||
if (days >= 7 && days <= 30) {
|
||||
const weeks = Math.floor(days / 7);
|
||||
|
||||
return `${Math.floor(weeks)} week${weeks === 1 ? "" : "s"}`;
|
||||
}
|
||||
|
||||
if (days >= 1 && days <= 6) {
|
||||
return `${days} day${days > 1 ? "s" : ""}`;
|
||||
}
|
||||
|
||||
return `${hours >= 1 ? `${hours} hour${hours > 1 ? "s" : ""}` : ""} ${
|
||||
minutes >= 1 && hours === 0
|
||||
? `${minutes} minute${minutes > 1 ? "s" : ""}`
|
||||
: ""
|
||||
} ${
|
||||
seconds >= 1 && minutes === 0 && hours === 0
|
||||
? `${seconds} second${seconds > 1 ? "s" : ""}`
|
||||
: ""
|
||||
}`;
|
||||
};
|
||||
|
||||
export const getTimeFromTimestamp = (
|
||||
timestamp: string,
|
||||
fullDate: boolean = false
|
||||
) => {
|
||||
const dateObject = new Date(parseInt(timestamp) * 1000);
|
||||
|
||||
if (fullDate) {
|
||||
return `${dateObject.getFullYear()}-${String(
|
||||
dateObject.getMonth() + 1
|
||||
).padStart(2, "0")}-${String(dateObject.getDay()).padStart(
|
||||
2,
|
||||
"0"
|
||||
)} ${dateObject.getHours()}:${String(dateObject.getMinutes()).padStart(
|
||||
2,
|
||||
"0"
|
||||
)}:${String(dateObject.getSeconds()).padStart(2, "0")}`;
|
||||
}
|
||||
return `${dateObject.getHours()}:${String(dateObject.getMinutes()).padStart(
|
||||
2,
|
||||
"0"
|
||||
)}`;
|
||||
};
|
||||
|
||||
export const calculateBytes = (
|
||||
x: string,
|
||||
showDecimals = false,
|
||||
roundFloor = true
|
||||
) => {
|
||||
const bytes = parseInt(x, 10);
|
||||
|
||||
if (bytes === 0) {
|
||||
return { total: 0, unit: k8sCalcUnits[0] };
|
||||
}
|
||||
|
||||
// Gi : GiB
|
||||
const k = 1024;
|
||||
|
||||
// Get unit for measure
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
const fractionDigits = showDecimals ? 0 : 1;
|
||||
|
||||
const bytesUnit = bytes / Math.pow(k, i);
|
||||
|
||||
const roundedUnit = roundFloor ? Math.floor(bytesUnit) : bytesUnit;
|
||||
|
||||
// Get Unit parsed
|
||||
const unitParsed = parseFloat(roundedUnit.toFixed(fractionDigits));
|
||||
const finalUnit = k8sCalcUnits[i];
|
||||
|
||||
return { total: unitParsed, unit: finalUnit };
|
||||
};
|
||||
|
||||
export const nsToSeconds = (nanoseconds: number) => {
|
||||
const conversion = nanoseconds * 0.000000001;
|
||||
const round = Math.round((conversion + Number.EPSILON) * 10000) / 10000;
|
||||
|
||||
return `${round} s`;
|
||||
};
|
||||
|
||||
export const textToRGBColor = (text: string) => {
|
||||
const splitText = text.split("");
|
||||
|
||||
const hashVl = splitText.reduce((acc, currItem) => {
|
||||
return acc + currItem.charCodeAt(0) + ((acc << 5) - acc);
|
||||
}, 0);
|
||||
|
||||
const hashColored = ((hashVl * 100) & 0x00ffffff).toString(16).toUpperCase();
|
||||
|
||||
return `#${hashColored.padStart(6, "0")}`;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,8 @@ import configureStore from "./store";
|
||||
import * as serviceWorker from "./serviceWorker";
|
||||
import { ThemeProvider, withStyles } from "@material-ui/core/styles";
|
||||
import "react-virtualized/styles.css";
|
||||
import "react-grid-layout/css/styles.css";
|
||||
import "react-resizable/css/styles.css";
|
||||
|
||||
import "./index.css";
|
||||
import theme from "./theme/main";
|
||||
|
||||
@@ -103,9 +103,10 @@ const Account = ({ classes }: IServiceAccountsProps) => {
|
||||
newServiceAccount,
|
||||
setNewServiceAccount,
|
||||
] = useState<NewServiceAccount | null>(null);
|
||||
const [changePasswordModalOpen, setChangePasswordModalOpen] = useState<
|
||||
boolean
|
||||
>(false);
|
||||
const [
|
||||
changePasswordModalOpen,
|
||||
setChangePasswordModalOpen,
|
||||
] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchRecords();
|
||||
|
||||
@@ -200,7 +200,24 @@ const ListObjects = ({
|
||||
.invoke("GET", `/api/v1/buckets/${bucketName}/objects${extraPath}`)
|
||||
.then((res: BucketObjectsList) => {
|
||||
setSelectedBucket(bucketName);
|
||||
setRecords(res.objects || []);
|
||||
|
||||
const records: BucketObject[] = res.objects || [];
|
||||
const folders: BucketObject[] = [];
|
||||
const files: BucketObject[] = [];
|
||||
|
||||
records.forEach((record) => {
|
||||
// this is a folder
|
||||
if (record.name.endsWith("/")) {
|
||||
folders.push(record);
|
||||
} else {
|
||||
// this is a file
|
||||
files.push(record);
|
||||
}
|
||||
});
|
||||
|
||||
const recordsInElement = [...folders, ...files];
|
||||
|
||||
setRecords(recordsInElement);
|
||||
// In case no objects were retrieved, We check if item is a file
|
||||
if (!res.objects && extraPath !== "") {
|
||||
verifyIfIsFile();
|
||||
|
||||
@@ -216,9 +216,10 @@ const ViewBucket = ({ classes, match }: IViewBucketProps) => {
|
||||
);
|
||||
const [curTab, setCurTab] = useState<number>(0);
|
||||
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
|
||||
const [enableEncryptionScreenOpen, setEnableEncryptionScreenOpen] = useState<
|
||||
boolean
|
||||
>(false);
|
||||
const [
|
||||
enableEncryptionScreenOpen,
|
||||
setEnableEncryptionScreenOpen,
|
||||
] = useState<boolean>(false);
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const [selectedEvent, setSelectedEvent] = useState<BucketEvent | null>(null);
|
||||
const [bucketSize, setBucketSize] = useState<string>("0");
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
// 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/>.
|
||||
|
||||
import React from "react";
|
||||
import MomentUtils from "@date-io/moment";
|
||||
import { DateTimePicker, MuiPickersUtilsProvider } from "@material-ui/pickers";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import ScheduleIcon from "@material-ui/icons/Schedule";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
|
||||
interface IDateTimePicker {
|
||||
value: any;
|
||||
onChange: (value: any) => any;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
dateSelectorOverride: {
|
||||
height: 40,
|
||||
border: "#EAEDEE 1px solid",
|
||||
marginLeft: 15,
|
||||
backgroundColor: "#fff",
|
||||
padding: "0 16px",
|
||||
borderRadius: 5,
|
||||
"&.MuiInput-underline:hover:not(.Mui-disabled):before": {
|
||||
borderBottom: 0,
|
||||
},
|
||||
"&:hover": {
|
||||
borderColor: "#000",
|
||||
"&:before, &:after": {
|
||||
borderColor: "transparent",
|
||||
borderBottom: 0,
|
||||
},
|
||||
},
|
||||
"&:before, &:after": {
|
||||
borderColor: "transparent",
|
||||
borderBottom: 0,
|
||||
},
|
||||
"& input": {
|
||||
fontSize: 12,
|
||||
fontWeight: 600,
|
||||
color: "#393939",
|
||||
},
|
||||
},
|
||||
parentDateOverride: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const DateTimePickerWrapper = ({
|
||||
value,
|
||||
onChange,
|
||||
classes,
|
||||
}: IDateTimePicker) => {
|
||||
return (
|
||||
<MuiPickersUtilsProvider utils={MomentUtils}>
|
||||
<DateTimePicker
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<ScheduleIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
className: classes.dateSelectorOverride,
|
||||
}}
|
||||
label=""
|
||||
ampm={false}
|
||||
variant={"inline"}
|
||||
className={classes.parentDateOverride}
|
||||
format="MMMM Do YYYY, h:mm a"
|
||||
/>
|
||||
</MuiPickersUtilsProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(DateTimePickerWrapper);
|
||||
@@ -0,0 +1,106 @@
|
||||
// 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/>.
|
||||
|
||||
import React, { Fragment } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import { searchField } from "../common/styleLibrary";
|
||||
|
||||
interface IFilterInputWrapper {
|
||||
classes: any;
|
||||
value: string;
|
||||
onChange: (txtVar: string) => any;
|
||||
label: string;
|
||||
placeholder?: string;
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
searchField: {
|
||||
...searchField.searchField,
|
||||
height: 30,
|
||||
padding: 0,
|
||||
"& input": {
|
||||
padding: "0 12px",
|
||||
height: 28,
|
||||
fontSize: 12,
|
||||
fontWeight: 600,
|
||||
color: "#393939",
|
||||
},
|
||||
"&.isDisabled": {
|
||||
"&:hover": {
|
||||
borderColor: "#EAEDEE",
|
||||
},
|
||||
},
|
||||
"& input.Mui-disabled": {
|
||||
backgroundColor: "#EAEAEA",
|
||||
},
|
||||
},
|
||||
labelStyle: {
|
||||
color: "#393939",
|
||||
fontSize: 12,
|
||||
marginBottom: 4,
|
||||
},
|
||||
buttonKit: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
toggleButton: {
|
||||
marginRight: 10,
|
||||
},
|
||||
fieldContainer: {
|
||||
flexGrow: 1,
|
||||
margin: "0 15px",
|
||||
},
|
||||
});
|
||||
|
||||
const FilterInputWrapper = ({
|
||||
classes,
|
||||
label,
|
||||
onChange,
|
||||
value,
|
||||
placeholder = "",
|
||||
id,
|
||||
name,
|
||||
}: IFilterInputWrapper) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={classes.fieldContainer}>
|
||||
<div className={classes.labelStyle}>{label}</div>
|
||||
<div className={classes.buttonKit}>
|
||||
<TextField
|
||||
placeholder={placeholder}
|
||||
id={id}
|
||||
name={name}
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
onChange(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
}}
|
||||
className={classes.searchField}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(FilterInputWrapper);
|
||||
@@ -34,6 +34,7 @@ interface IFormSwitch {
|
||||
indicatorLabels?: string[];
|
||||
checked: boolean;
|
||||
switchOnly?: boolean;
|
||||
containerClass?: string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -177,11 +178,12 @@ const FormSwitchWrapper = ({
|
||||
switchOnly = false,
|
||||
tooltip = "",
|
||||
indicatorLabels = [],
|
||||
containerClass = "",
|
||||
classes,
|
||||
}: IFormSwitch) => {
|
||||
const switchComponent = (
|
||||
<React.Fragment>
|
||||
<div className={classes.switchContainer}>
|
||||
<div className={`${classes.switchContainer} ${containerClass}`}>
|
||||
<StyledSwitch
|
||||
checked={checked}
|
||||
onChange={onChange}
|
||||
|
||||
@@ -135,9 +135,22 @@ export const containerForHeader = (bottomSpacing: any) => ({
|
||||
});
|
||||
|
||||
export const actionsTray = {
|
||||
label: {
|
||||
color: "#393939",
|
||||
fontWeight: 600,
|
||||
fontSize: 13,
|
||||
alignSelf: "center" as const,
|
||||
whiteSpace: "nowrap" as const,
|
||||
"&:not(:first-of-type)": {
|
||||
marginLeft: 10,
|
||||
},
|
||||
},
|
||||
timeContainers: {
|
||||
height: 40,
|
||||
},
|
||||
actionsTray: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
display: "flex" as const,
|
||||
justifyContent: "space-between" as const,
|
||||
"& button": {
|
||||
flexGrow: 0,
|
||||
marginLeft: 15,
|
||||
@@ -292,3 +305,105 @@ export const settingsCommon = {
|
||||
margin: "20px 38px 0",
|
||||
},
|
||||
};
|
||||
|
||||
export const logsCommon = {
|
||||
logsSubContainer: {
|
||||
height: "calc(100vh - 230px)",
|
||||
padding: "15px 33px",
|
||||
},
|
||||
};
|
||||
|
||||
export const widgetCommon = {
|
||||
singleValueContainer: {
|
||||
position: "relative" as const,
|
||||
flexGrow: 1,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
border: "#EAEAEA 1px solid",
|
||||
borderRadius: 5,
|
||||
backgroundColor: "#fff",
|
||||
},
|
||||
titleContainer: {
|
||||
color: "#393939",
|
||||
fontWeight: 600,
|
||||
height: 15,
|
||||
textAlign: "center" as const,
|
||||
fontSize: 10,
|
||||
},
|
||||
contentContainer: {
|
||||
flexGrow: 2,
|
||||
justifyContent: "center" as const,
|
||||
alignItems: "center" as const,
|
||||
display: "flex" as const,
|
||||
position: "absolute" as const,
|
||||
width: "100%",
|
||||
height: "calc(100% - 15px)",
|
||||
},
|
||||
contentContainerWithLabel: {
|
||||
height: "calc(100% - 25px)",
|
||||
},
|
||||
legendBlock: {
|
||||
position: "absolute" as const,
|
||||
bottom: 5,
|
||||
display: "flex" as const,
|
||||
width: "100%",
|
||||
height: 15,
|
||||
flexWrap: "wrap" as const,
|
||||
overflowY: "auto" as const,
|
||||
},
|
||||
singleLegendContainer: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "0 10px",
|
||||
maxWidth: "100%",
|
||||
},
|
||||
colorContainer: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
minWidth: 8,
|
||||
borderRadius: "100%",
|
||||
marginRight: 5,
|
||||
},
|
||||
legendLabel: {
|
||||
fontSize: "80%",
|
||||
color: "#393939",
|
||||
whiteSpace: "nowrap" as const,
|
||||
overflow: "hidden" as const,
|
||||
textOverflow: "ellipsis" as const,
|
||||
},
|
||||
};
|
||||
|
||||
export const tooltipCommon = {
|
||||
customTooltip: {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.90)",
|
||||
border: "#eaeaea 1px solid",
|
||||
borderRadius: 3,
|
||||
padding: "5px 10px",
|
||||
maxHeight: 300,
|
||||
overflowY: "auto" as const,
|
||||
},
|
||||
labelContainer: {
|
||||
display: "flex" as const,
|
||||
alignItems: "center" as const,
|
||||
},
|
||||
labelColor: {
|
||||
width: 6,
|
||||
height: 6,
|
||||
display: "block" as const,
|
||||
borderRadius: "100%",
|
||||
marginRight: 5,
|
||||
},
|
||||
itemValue: {
|
||||
fontSize: "75%",
|
||||
color: "#393939",
|
||||
},
|
||||
valueContainer: {
|
||||
fontWeight: 600,
|
||||
},
|
||||
timeStampTitle: {
|
||||
fontSize: "80%",
|
||||
color: "#9c9c9c",
|
||||
textAlign: "center" as const,
|
||||
marginBottom: 6,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
//
|
||||
// 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/>.
|
||||
import React from "react";
|
||||
import React, { useState, Fragment } from "react";
|
||||
import get from "lodash/get";
|
||||
import isString from "lodash/isString";
|
||||
import {
|
||||
@@ -22,15 +22,21 @@ import {
|
||||
Grid,
|
||||
Checkbox,
|
||||
Typography,
|
||||
IconButton,
|
||||
Popover,
|
||||
} from "@material-ui/core";
|
||||
import { Table, Column, AutoSizer } from "react-virtualized";
|
||||
import { Table, Column, AutoSizer, InfiniteLoader } from "react-virtualized";
|
||||
import { createStyles, withStyles } from "@material-ui/core/styles";
|
||||
import ViewColumnIcon from "@material-ui/icons/ViewColumn";
|
||||
import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown";
|
||||
import ArrowDropUpIcon from "@material-ui/icons/ArrowDropUp";
|
||||
import TableActionButton from "./TableActionButton";
|
||||
import history from "../../../../history";
|
||||
import {
|
||||
checkboxIcons,
|
||||
radioIcons,
|
||||
} from "../FormComponents/common/styleLibrary";
|
||||
import CheckboxWrapper from "../FormComponents/CheckboxWrapper/CheckboxWrapper";
|
||||
|
||||
//Interfaces for table Items
|
||||
|
||||
@@ -51,6 +57,21 @@ interface IColumns {
|
||||
width?: number;
|
||||
headerTextAlign?: string;
|
||||
contentTextAlign?: string;
|
||||
enableSort?: boolean;
|
||||
}
|
||||
|
||||
interface IInfiniteScrollConfig {
|
||||
loadMoreRecords: (indexElements: {
|
||||
startIndex: number;
|
||||
stopIndex: number;
|
||||
}) => Promise<any>;
|
||||
recordsCount: number;
|
||||
}
|
||||
|
||||
interface ISortConfig {
|
||||
triggerSort: (val: any) => any;
|
||||
currentSort: string;
|
||||
currentDirection: "ASC" | "DESC" | undefined;
|
||||
}
|
||||
|
||||
interface TableWrapperProps {
|
||||
@@ -67,6 +88,13 @@ interface TableWrapperProps {
|
||||
customEmptyMessage?: string;
|
||||
customPaperHeight?: string;
|
||||
noBackground?: boolean;
|
||||
columnsSelector?: boolean;
|
||||
textSelectable?: boolean;
|
||||
columnsShown?: string[];
|
||||
onColumnChange?: (column: string, state: boolean) => any;
|
||||
autoScrollToBottom?: boolean;
|
||||
infiniteScrollConfig?: IInfiniteScrollConfig;
|
||||
sortConfig?: ISortConfig;
|
||||
}
|
||||
|
||||
const borderColor = "#9c9c9c80";
|
||||
@@ -98,7 +126,7 @@ const styles = () =>
|
||||
borderRadius: 3,
|
||||
minHeight: 200,
|
||||
overflowY: "scroll",
|
||||
|
||||
position: "relative",
|
||||
"&::-webkit-scrollbar": {
|
||||
width: 3,
|
||||
height: 3,
|
||||
@@ -169,6 +197,26 @@ const styles = () =>
|
||||
paddingTop: "100px",
|
||||
paddingBottom: "100px",
|
||||
},
|
||||
overlayColumnSelection: {
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
top: 0,
|
||||
},
|
||||
popoverContainer: {
|
||||
position: "relative",
|
||||
},
|
||||
popoverContent: {
|
||||
maxHeight: 250,
|
||||
overflowY: "auto",
|
||||
padding: "0 10px 10px",
|
||||
},
|
||||
shownColumnsLabel: {
|
||||
color: "#9c9c9c",
|
||||
fontSize: 12,
|
||||
padding: 10,
|
||||
borderBottom: "#eaeaea 1px solid",
|
||||
width: "100%",
|
||||
},
|
||||
"@global": {
|
||||
".rowLine": {
|
||||
borderBottom: `1px solid ${borderColor}`,
|
||||
@@ -186,6 +234,9 @@ const styles = () =>
|
||||
"&.canClick": {
|
||||
cursor: "pointer",
|
||||
},
|
||||
"&.canSelectText": {
|
||||
userSelect: "text",
|
||||
},
|
||||
},
|
||||
"& .selected": {
|
||||
color: "#081C42",
|
||||
@@ -197,6 +248,9 @@ const styles = () =>
|
||||
fontWeight: 700,
|
||||
fontSize: 14,
|
||||
fontStyle: "initial",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
outline: "none",
|
||||
},
|
||||
".ReactVirtualized__Table__headerRow": {
|
||||
fontWeight: 700,
|
||||
@@ -236,9 +290,9 @@ const subRenderFunction = (
|
||||
: renderConst; // If render function is set, we send the value to the function.
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
<span className={isSelected ? "selected" : ""}>{renderElement}</span>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -248,8 +302,18 @@ const calculateColumnRest = (
|
||||
containerWidth: number,
|
||||
actionsWidth: number,
|
||||
hasSelect: boolean,
|
||||
hasActions: boolean
|
||||
hasActions: boolean,
|
||||
columnsSelector: boolean,
|
||||
columnsShown: string[]
|
||||
) => {
|
||||
let colsItems = [...columns];
|
||||
|
||||
if (columnsSelector) {
|
||||
colsItems = columns.filter((column) =>
|
||||
columnsShown.includes(column.elementKey)
|
||||
);
|
||||
}
|
||||
|
||||
let initialValue = containerWidth;
|
||||
|
||||
if (hasSelect) {
|
||||
@@ -260,11 +324,11 @@ const calculateColumnRest = (
|
||||
initialValue -= actionsWidth;
|
||||
}
|
||||
|
||||
let freeSpacing = columns.reduce((total, currValue) => {
|
||||
let freeSpacing = colsItems.reduce((total, currValue) => {
|
||||
return currValue.width ? total - currValue.width : total;
|
||||
}, initialValue);
|
||||
|
||||
return freeSpacing / columns.filter((el) => !el.width).length;
|
||||
return freeSpacing / colsItems.filter((el) => !el.width).length;
|
||||
};
|
||||
|
||||
// Function that renders Columns in table
|
||||
@@ -275,16 +339,28 @@ const generateColumnsMap = (
|
||||
hasSelect: boolean,
|
||||
hasActions: boolean,
|
||||
selectedItems: string[],
|
||||
idField: string
|
||||
idField: string,
|
||||
columnsSelector: boolean,
|
||||
columnsShown: string[],
|
||||
sortColumn: string,
|
||||
sortDirection: "ASC" | "DESC" | undefined
|
||||
) => {
|
||||
const commonRestWidth = calculateColumnRest(
|
||||
columns,
|
||||
containerWidth,
|
||||
actionsWidth,
|
||||
hasSelect,
|
||||
hasActions
|
||||
hasActions,
|
||||
columnsSelector,
|
||||
columnsShown
|
||||
);
|
||||
return columns.map((column: IColumns, index: number) => {
|
||||
if (columnsSelector && !columnsShown.includes(column.elementKey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const disableSort = column.enableSort ? !column.enableSort : true;
|
||||
|
||||
return (
|
||||
<Column
|
||||
key={`col-tb-${index.toString()}`}
|
||||
@@ -292,7 +368,20 @@ const generateColumnsMap = (
|
||||
headerClassName={`titleHeader ${
|
||||
column.headerTextAlign ? `text-${column.headerTextAlign}` : ""
|
||||
}`}
|
||||
headerRenderer={() => <React.Fragment>{column.label}</React.Fragment>}
|
||||
headerRenderer={() => (
|
||||
<Fragment>
|
||||
{sortColumn === column.elementKey && (
|
||||
<Fragment>
|
||||
{sortDirection === "ASC" ? (
|
||||
<ArrowDropUpIcon />
|
||||
) : (
|
||||
<ArrowDropDownIcon />
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
{column.label}
|
||||
</Fragment>
|
||||
)}
|
||||
className={
|
||||
column.contentTextAlign ? `text-${column.contentTextAlign}` : ""
|
||||
}
|
||||
@@ -305,6 +394,8 @@ const generateColumnsMap = (
|
||||
return subRenderFunction(rowData, column, isSelected);
|
||||
}}
|
||||
width={column.width || commonRestWidth}
|
||||
disableSort={disableSort}
|
||||
defaultSortDirection={"ASC"}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@@ -368,7 +459,17 @@ const TableWrapper = ({
|
||||
customEmptyMessage = "",
|
||||
customPaperHeight = "",
|
||||
noBackground = false,
|
||||
columnsSelector = false,
|
||||
textSelectable = false,
|
||||
columnsShown = [],
|
||||
onColumnChange = (column: string, state: boolean) => {},
|
||||
infiniteScrollConfig,
|
||||
sortConfig,
|
||||
autoScrollToBottom = false,
|
||||
}: TableWrapperProps) => {
|
||||
const [columnSelectorOpen, setColumnSelectorOpen] = useState<boolean>(false);
|
||||
const [anchorEl, setAnchorEl] = React.useState<any>(null);
|
||||
|
||||
const findView = itemActions
|
||||
? itemActions.find((el) => el.type === "view")
|
||||
: null;
|
||||
@@ -387,6 +488,64 @@ const TableWrapper = ({
|
||||
}
|
||||
};
|
||||
|
||||
const openColumnsSelector = (event: { currentTarget: any }) => {
|
||||
setColumnSelectorOpen(!columnSelectorOpen);
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const closeColumnSelector = () => {
|
||||
setColumnSelectorOpen(false);
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const columnsSelection = (columns: IColumns[]) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<IconButton
|
||||
aria-describedby={"columnsSelector"}
|
||||
color="primary"
|
||||
onClick={openColumnsSelector}
|
||||
>
|
||||
<ViewColumnIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
<Popover
|
||||
anchorEl={anchorEl}
|
||||
id={"columnsSelector"}
|
||||
open={columnSelectorOpen}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
}}
|
||||
onClose={closeColumnSelector}
|
||||
className={classes.popoverContainer}
|
||||
>
|
||||
<div className={classes.shownColumnsLabel}>Shown Columns</div>
|
||||
<div className={classes.popoverContent}>
|
||||
{columns.map((column: IColumns) => {
|
||||
return (
|
||||
<CheckboxWrapper
|
||||
key={`tableColumns-${column.label}`}
|
||||
label={column.label}
|
||||
checked={columnsShown.includes(column.elementKey)}
|
||||
onChange={(e) => {
|
||||
onColumnChange(column.elementKey, e.target.checked);
|
||||
}}
|
||||
id={`chbox-${column.label}`}
|
||||
name={`chbox-${column.label}`}
|
||||
value={column.label}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Popover>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid item xs={12}>
|
||||
<Paper
|
||||
@@ -408,136 +567,168 @@ const TableWrapper = ({
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
{columnsSelector && !isLoading && records.length > 0 && (
|
||||
<div className={classes.overlayColumnSelection}>
|
||||
{columnsSelection(columns)}
|
||||
</div>
|
||||
)}
|
||||
{records && !isLoading && records.length > 0 ? (
|
||||
<AutoSizer>
|
||||
{({ width, height }: any) => {
|
||||
const optionsWidth = calculateOptionsSize(
|
||||
width,
|
||||
itemActions
|
||||
? itemActions.filter((el) => el.type !== "view").length
|
||||
: 0
|
||||
);
|
||||
const hasSelect: boolean = !!(onSelect && selectedItems);
|
||||
const hasOptions: boolean = !!(
|
||||
(itemActions && itemActions.length > 1) ||
|
||||
(itemActions &&
|
||||
itemActions.length === 1 &&
|
||||
itemActions[0].type !== "view")
|
||||
);
|
||||
return (
|
||||
<Table
|
||||
ref="Table"
|
||||
disableHeader={false}
|
||||
headerClassName={"headerItem"}
|
||||
headerHeight={40}
|
||||
height={height}
|
||||
noRowsRenderer={() => (
|
||||
<React.Fragment>
|
||||
{customEmptyMessage !== ""
|
||||
? customEmptyMessage
|
||||
: `There are no ${entityName} yet.`}
|
||||
</React.Fragment>
|
||||
)}
|
||||
overscanRowCount={10}
|
||||
rowHeight={40}
|
||||
width={width}
|
||||
rowCount={records.length}
|
||||
rowGetter={({ index }) => records[index]}
|
||||
onRowClick={({ rowData }) => {
|
||||
clickAction(rowData);
|
||||
}}
|
||||
rowClassName={`rowLine ${findView ? "canClick" : ""}`}
|
||||
>
|
||||
{hasSelect && (
|
||||
<Column
|
||||
headerRenderer={() => (
|
||||
<React.Fragment>Select</React.Fragment>
|
||||
)}
|
||||
dataKey={idField}
|
||||
width={selectWidth}
|
||||
cellRenderer={({ rowData }) => {
|
||||
const isSelected = selectedItems
|
||||
? selectedItems.includes(
|
||||
isString(rowData) ? rowData : rowData[idField]
|
||||
)
|
||||
: false;
|
||||
|
||||
return (
|
||||
<Checkbox
|
||||
value={
|
||||
isString(rowData) ? rowData : rowData[idField]
|
||||
}
|
||||
color="primary"
|
||||
inputProps={{
|
||||
"aria-label": "secondary checkbox",
|
||||
}}
|
||||
checked={isSelected}
|
||||
onChange={onSelect}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
checkedIcon={
|
||||
<span
|
||||
className={
|
||||
radioSelection
|
||||
? classes.radioSelectedIcon
|
||||
: classes.checkedIcon
|
||||
}
|
||||
/>
|
||||
}
|
||||
icon={
|
||||
<span
|
||||
className={
|
||||
radioSelection
|
||||
? classes.radioUnselectedIcon
|
||||
: classes.unCheckedIcon
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{generateColumnsMap(
|
||||
columns,
|
||||
<InfiniteLoader
|
||||
isRowLoaded={({ index }) => !!records[index]}
|
||||
loadMoreRows={
|
||||
infiniteScrollConfig
|
||||
? infiniteScrollConfig.loadMoreRecords
|
||||
: () => new Promise(() => true)
|
||||
}
|
||||
rowCount={
|
||||
infiniteScrollConfig
|
||||
? infiniteScrollConfig.recordsCount
|
||||
: records.length
|
||||
}
|
||||
>
|
||||
{({ onRowsRendered, registerChild }) => (
|
||||
<AutoSizer>
|
||||
{({ width, height }: any) => {
|
||||
const optionsWidth = calculateOptionsSize(
|
||||
width,
|
||||
optionsWidth,
|
||||
hasSelect,
|
||||
hasOptions,
|
||||
selectedItems || [],
|
||||
idField
|
||||
)}
|
||||
{hasOptions && (
|
||||
<Column
|
||||
headerRenderer={() => (
|
||||
<React.Fragment>Options</React.Fragment>
|
||||
itemActions
|
||||
? itemActions.filter((el) => el.type !== "view").length
|
||||
: 0
|
||||
);
|
||||
const hasSelect: boolean = !!(onSelect && selectedItems);
|
||||
const hasOptions: boolean = !!(
|
||||
(itemActions && itemActions.length > 1) ||
|
||||
(itemActions &&
|
||||
itemActions.length === 1 &&
|
||||
itemActions[0].type !== "view")
|
||||
);
|
||||
return (
|
||||
<Table
|
||||
ref={registerChild}
|
||||
disableHeader={false}
|
||||
headerClassName={"headerItem"}
|
||||
headerHeight={40}
|
||||
height={height}
|
||||
noRowsRenderer={() => (
|
||||
<Fragment>
|
||||
{customEmptyMessage !== ""
|
||||
? customEmptyMessage
|
||||
: `There are no ${entityName} yet.`}
|
||||
</Fragment>
|
||||
)}
|
||||
dataKey={idField}
|
||||
width={optionsWidth}
|
||||
headerClassName="optionsAlignment"
|
||||
className="optionsAlignment"
|
||||
cellRenderer={({ rowData }) => {
|
||||
const isSelected = selectedItems
|
||||
? selectedItems.includes(
|
||||
isString(rowData) ? rowData : rowData[idField]
|
||||
)
|
||||
: false;
|
||||
return elementActions(
|
||||
itemActions || [],
|
||||
rowData,
|
||||
isSelected,
|
||||
idField
|
||||
);
|
||||
overscanRowCount={10}
|
||||
rowHeight={40}
|
||||
width={width}
|
||||
rowCount={records.length}
|
||||
rowGetter={({ index }) => records[index]}
|
||||
onRowClick={({ rowData }) => {
|
||||
clickAction(rowData);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Table>
|
||||
);
|
||||
}}
|
||||
</AutoSizer>
|
||||
rowClassName={`rowLine ${findView ? "canClick" : ""} ${
|
||||
!findView && textSelectable ? "canSelectText" : ""
|
||||
}`}
|
||||
onRowsRendered={onRowsRendered}
|
||||
sort={sortConfig ? sortConfig.triggerSort : undefined}
|
||||
sortBy={sortConfig ? sortConfig.currentSort : undefined}
|
||||
sortDirection={
|
||||
sortConfig ? sortConfig.currentDirection : undefined
|
||||
}
|
||||
scrollToIndex={
|
||||
autoScrollToBottom ? records.length - 1 : -1
|
||||
}
|
||||
>
|
||||
{hasSelect && (
|
||||
<Column
|
||||
headerRenderer={() => <Fragment>Select</Fragment>}
|
||||
dataKey={idField}
|
||||
width={selectWidth}
|
||||
cellRenderer={({ rowData }) => {
|
||||
const isSelected = selectedItems
|
||||
? selectedItems.includes(
|
||||
isString(rowData) ? rowData : rowData[idField]
|
||||
)
|
||||
: false;
|
||||
|
||||
return (
|
||||
<Checkbox
|
||||
value={
|
||||
isString(rowData) ? rowData : rowData[idField]
|
||||
}
|
||||
color="primary"
|
||||
inputProps={{
|
||||
"aria-label": "secondary checkbox",
|
||||
}}
|
||||
checked={isSelected}
|
||||
onChange={onSelect}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
checkedIcon={
|
||||
<span
|
||||
className={
|
||||
radioSelection
|
||||
? classes.radioSelectedIcon
|
||||
: classes.checkedIcon
|
||||
}
|
||||
/>
|
||||
}
|
||||
icon={
|
||||
<span
|
||||
className={
|
||||
radioSelection
|
||||
? classes.radioUnselectedIcon
|
||||
: classes.unCheckedIcon
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{generateColumnsMap(
|
||||
columns,
|
||||
width,
|
||||
optionsWidth,
|
||||
hasSelect,
|
||||
hasOptions,
|
||||
selectedItems || [],
|
||||
idField,
|
||||
columnsSelector,
|
||||
columnsShown,
|
||||
sortConfig ? sortConfig.currentSort : "",
|
||||
sortConfig ? sortConfig.currentDirection : undefined
|
||||
)}
|
||||
{hasOptions && (
|
||||
<Column
|
||||
headerRenderer={() => <Fragment>Options</Fragment>}
|
||||
dataKey={idField}
|
||||
width={optionsWidth}
|
||||
headerClassName="optionsAlignment"
|
||||
className="optionsAlignment"
|
||||
cellRenderer={({ rowData }) => {
|
||||
const isSelected = selectedItems
|
||||
? selectedItems.includes(
|
||||
isString(rowData) ? rowData : rowData[idField]
|
||||
)
|
||||
: false;
|
||||
return elementActions(
|
||||
itemActions || [],
|
||||
rowData,
|
||||
isSelected,
|
||||
idField
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Table>
|
||||
);
|
||||
}}
|
||||
</AutoSizer>
|
||||
)}
|
||||
</InfiniteLoader>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
{!isLoading && (
|
||||
<div>
|
||||
{customEmptyMessage !== ""
|
||||
@@ -545,7 +736,7 @@ const TableWrapper = ({
|
||||
: `There are no ${entityName} yet.`}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
)}
|
||||
</Paper>
|
||||
</Grid>
|
||||
|
||||
@@ -21,6 +21,7 @@ import { Button, LinearProgress } from "@material-ui/core";
|
||||
import CssBaseline from "@material-ui/core/CssBaseline";
|
||||
import Drawer from "@material-ui/core/Drawer";
|
||||
import Container from "@material-ui/core/Container";
|
||||
import Snackbar from "@material-ui/core/Snackbar";
|
||||
import history from "../../history";
|
||||
import { Redirect, Route, Router, Switch } from "react-router-dom";
|
||||
import { connect } from "react-redux";
|
||||
@@ -48,10 +49,9 @@ import ObjectBrowser from "./ObjectBrowser/ObjectBrowser";
|
||||
import ObjectRouting from "./Buckets/ListBuckets/Objects/ListObjects/ObjectRouting";
|
||||
import License from "./License/License";
|
||||
import Trace from "./Trace/Trace";
|
||||
import Logs from "./Logs/Logs";
|
||||
import LogsMain from "./Logs/LogsMain";
|
||||
import Heal from "./Heal/Heal";
|
||||
import Watch from "./Watch/Watch";
|
||||
import Snackbar from "@material-ui/core/Snackbar";
|
||||
|
||||
const drawerWidth = 245;
|
||||
|
||||
@@ -285,7 +285,7 @@ const Console = ({
|
||||
path: "/trace",
|
||||
},
|
||||
{
|
||||
component: Logs,
|
||||
component: LogsMain,
|
||||
path: "/logs",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
// 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/>.
|
||||
|
||||
import React, { Fragment } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import clsx from "clsx";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { Usage } from "../types";
|
||||
import { niceBytes } from "../../../../common/utils";
|
||||
import { containerForHeader } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import AllBucketsIcon from "../../../../icons/AllBucketsIcon";
|
||||
import UsageIcon from "../../../../icons/UsageIcon";
|
||||
import EgressIcon from "../../../../icons/EgressIcon";
|
||||
import ErrorBlock from "../../../shared/ErrorBlock";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
paper: {
|
||||
padding: theme.spacing(2),
|
||||
display: "flex",
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
border: "#eaedee 1px solid",
|
||||
borderRadius: 5,
|
||||
boxShadow: "none",
|
||||
},
|
||||
fixedHeight: {
|
||||
height: 165,
|
||||
minWidth: 247,
|
||||
marginRight: 20,
|
||||
padding: "25px 28px",
|
||||
"& svg": {
|
||||
maxHeight: 18,
|
||||
},
|
||||
},
|
||||
consumptionValue: {
|
||||
color: "#000000",
|
||||
fontSize: "60px",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
icon: {
|
||||
marginRight: 10,
|
||||
color: "#777777",
|
||||
},
|
||||
notationContainer: {
|
||||
display: "flex",
|
||||
},
|
||||
dashboardBG: {
|
||||
width: 390,
|
||||
height: 255,
|
||||
zIndex: 500,
|
||||
position: "absolute",
|
||||
backgroundSize: "fill",
|
||||
backgroundImage: "url(/images/BG_IllustrationDarker.svg)",
|
||||
backgroundPosition: "right bottom",
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundRepeat: "no-repeat",
|
||||
},
|
||||
dashboardContainer: {
|
||||
zIndex: 600,
|
||||
position: "absolute",
|
||||
},
|
||||
elementTitle: {
|
||||
fontWeight: 500,
|
||||
color: "#777777",
|
||||
fontSize: 14,
|
||||
marginTop: -9,
|
||||
},
|
||||
smallUnit: {
|
||||
fontSize: 20,
|
||||
},
|
||||
});
|
||||
|
||||
interface IDashboardProps {
|
||||
classes: any;
|
||||
usage: Usage | null;
|
||||
error: string;
|
||||
}
|
||||
|
||||
const BasicDashboard = ({ classes, usage, error }: IDashboardProps) => {
|
||||
const fixedHeightPaper = clsx(classes.paper, classes.fixedHeight);
|
||||
|
||||
const prettyUsage = (usage: string | undefined) => {
|
||||
if (usage === undefined) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
const niceBytesUsage = niceBytes(usage).split(" ");
|
||||
|
||||
if (niceBytesUsage.length !== 2) {
|
||||
return niceBytesUsage.join(" ");
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{niceBytesUsage[0]}
|
||||
<span className={classes.smallUnit}>{niceBytesUsage[1]}</span>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const prettyNumber = (usage: number | undefined) => {
|
||||
if (usage === undefined) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return usage.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={classes.dashboardBG} />
|
||||
<Grid container className={classes.dashboardContainer}>
|
||||
<Grid container spacing={3} className={classes.container}>
|
||||
{error !== "" && (
|
||||
<Grid container>
|
||||
<ErrorBlock errorMessage={error} />
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item className={classes.notationContainer}>
|
||||
<Paper className={fixedHeightPaper}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item className={classes.icon}>
|
||||
<AllBucketsIcon />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography className={classes.elementTitle}>
|
||||
All buckets
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography className={classes.consumptionValue}>
|
||||
{usage ? prettyNumber(usage.buckets) : 0}
|
||||
</Typography>
|
||||
</Paper>
|
||||
<Paper className={fixedHeightPaper}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item className={classes.icon}>
|
||||
<UsageIcon />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography className={classes.elementTitle}>
|
||||
Usage
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography className={classes.consumptionValue}>
|
||||
{usage ? prettyUsage(usage.usage + "") : 0}
|
||||
</Typography>
|
||||
</Paper>
|
||||
<Paper className={fixedHeightPaper}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item className={classes.icon}>
|
||||
<EgressIcon />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography className={classes.elementTitle}>
|
||||
{" "}
|
||||
Total Objects
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography className={classes.consumptionValue}>
|
||||
{usage ? prettyNumber(usage.objects) : 0}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(BasicDashboard);
|
||||
@@ -14,139 +14,30 @@
|
||||
// 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/>.
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import clsx from "clsx";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { Usage } from "./types";
|
||||
import api from "../../../common/api";
|
||||
import { niceBytes } from "../../../common/utils";
|
||||
import { LinearProgress } from "@material-ui/core";
|
||||
import React, { useEffect, useState, Fragment } from "react";
|
||||
import get from "lodash/get";
|
||||
import PrDashboard from "./Prometheus/PrDashboard";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||
import AllBucketsIcon from "../../../icons/AllBucketsIcon";
|
||||
import UsageIcon from "../../../icons/UsageIcon";
|
||||
import EgressIcon from "../../../icons/EgressIcon";
|
||||
import ErrorBlock from "../../shared/ErrorBlock";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import BasicDashboard from "./BasicDashboard/BasicDashboard";
|
||||
import { LinearProgress } from "@material-ui/core";
|
||||
import api from "../../../common/api";
|
||||
import { Usage } from "./types";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
display: "flex",
|
||||
},
|
||||
toolbar: {
|
||||
paddingRight: 24, // keep right padding when drawer closed
|
||||
},
|
||||
toolbarIcon: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
padding: "0 8px",
|
||||
...theme.mixins.toolbar,
|
||||
},
|
||||
appBar: {
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
transition: theme.transitions.create(["width", "margin"], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
},
|
||||
|
||||
menuButton: {
|
||||
marginRight: 36,
|
||||
},
|
||||
menuButtonHidden: {
|
||||
display: "none",
|
||||
},
|
||||
title: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
drawerPaperClose: {
|
||||
overflowX: "hidden",
|
||||
transition: theme.transitions.create("width", {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
width: theme.spacing(7),
|
||||
[theme.breakpoints.up("sm")]: {
|
||||
width: theme.spacing(9),
|
||||
},
|
||||
},
|
||||
appBarSpacer: theme.mixins.toolbar,
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
height: "100vh",
|
||||
overflow: "auto",
|
||||
},
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
paper: {
|
||||
padding: theme.spacing(2),
|
||||
display: "flex",
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
border: "#eaedee 1px solid",
|
||||
borderRadius: 5,
|
||||
boxShadow: "none",
|
||||
},
|
||||
fixedHeight: {
|
||||
height: 165,
|
||||
minWidth: 247,
|
||||
marginRight: 20,
|
||||
padding: "25px 28px",
|
||||
"& svg": {
|
||||
maxHeight: 18,
|
||||
},
|
||||
},
|
||||
consumptionValue: {
|
||||
color: "#000000",
|
||||
fontSize: "60px",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
icon: {
|
||||
marginRight: 10,
|
||||
color: "#777777",
|
||||
},
|
||||
notationContainer: {
|
||||
display: "flex",
|
||||
},
|
||||
dashboardBG: {
|
||||
width: 390,
|
||||
height: 255,
|
||||
zIndex: 500,
|
||||
position: "absolute",
|
||||
backgroundSize: "fill",
|
||||
|
||||
backgroundImage: "url(/images/BG_IllustrationDarker.svg)",
|
||||
backgroundPosition: "right bottom",
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundRepeat: "no-repeat",
|
||||
},
|
||||
dashboardContainer: {
|
||||
zIndex: 600,
|
||||
position: "absolute",
|
||||
},
|
||||
elementTitle: {
|
||||
fontWeight: 500,
|
||||
color: "#777777",
|
||||
fontSize: 14,
|
||||
marginTop: -9,
|
||||
},
|
||||
smallUnit: {
|
||||
fontSize: 20,
|
||||
},
|
||||
});
|
||||
|
||||
interface IDashboardProps {
|
||||
interface IDashboardSimple {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const Dashboard = ({ classes }: IDashboardProps) => {
|
||||
const fixedHeightPaper = clsx(classes.paper, classes.fixedHeight);
|
||||
const [usage, setUsage] = useState<Usage | null>(null);
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
const Dashboard = ({ classes }: IDashboardSimple) => {
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [basicResult, setBasicResult] = useState<Usage | null>(null);
|
||||
const [error, setError] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
@@ -159,7 +50,7 @@ const Dashboard = ({ classes }: IDashboardProps) => {
|
||||
api
|
||||
.invoke("GET", `/api/v1/admin/info`)
|
||||
.then((res: Usage) => {
|
||||
setUsage(res);
|
||||
setBasicResult(res);
|
||||
setError("");
|
||||
setLoading(false);
|
||||
})
|
||||
@@ -168,103 +59,28 @@ const Dashboard = ({ classes }: IDashboardProps) => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
const prettyUsage = (usage: string | undefined) => {
|
||||
if (usage === undefined) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
const niceBytesUsage = niceBytes(usage).split(" ");
|
||||
|
||||
if (niceBytesUsage.length !== 2) {
|
||||
return niceBytesUsage.join(" ");
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{niceBytesUsage[0]}
|
||||
<span className={classes.smallUnit}>{niceBytesUsage[1]}</span>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const prettyNumber = (usage: number | undefined) => {
|
||||
if (usage === undefined) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return usage.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
};
|
||||
const widgets = get(basicResult, "widgets", null);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
<PageHeader label="Dashboard" />
|
||||
<div className={classes.dashboardBG} />
|
||||
<Grid container className={classes.dashboardContainer}>
|
||||
<Grid container spacing={3} className={classes.container}>
|
||||
{error !== "" && (
|
||||
<Grid container>
|
||||
<ErrorBlock errorMessage={error} />
|
||||
</Grid>
|
||||
)}
|
||||
{loading ? (
|
||||
<Grid item xs={12} md={12} lg={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Grid item className={classes.notationContainer}>
|
||||
<Paper className={fixedHeightPaper}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item className={classes.icon}>
|
||||
<AllBucketsIcon />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography className={classes.elementTitle}>
|
||||
All buckets
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography className={classes.consumptionValue}>
|
||||
{usage ? prettyNumber(usage.buckets) : 0}
|
||||
</Typography>
|
||||
</Paper>
|
||||
<Paper className={fixedHeightPaper}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item className={classes.icon}>
|
||||
<UsageIcon />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography className={classes.elementTitle}>
|
||||
Usage
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography className={classes.consumptionValue}>
|
||||
{usage ? prettyUsage(usage.usage + "") : 0}
|
||||
</Typography>
|
||||
</Paper>
|
||||
<Paper className={fixedHeightPaper}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item className={classes.icon}>
|
||||
<EgressIcon />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography className={classes.elementTitle}>
|
||||
{" "}
|
||||
Total Objects
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography className={classes.consumptionValue}>
|
||||
{usage ? prettyNumber(usage.objects) : 0}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid container>
|
||||
{loading ? (
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
) : (
|
||||
<Fragment>
|
||||
{widgets !== null ? (
|
||||
<PrDashboard />
|
||||
) : (
|
||||
<BasicDashboard usage={basicResult} error={error} />
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
// 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/>.
|
||||
|
||||
import React, {
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
Fragment,
|
||||
useCallback,
|
||||
} from "react";
|
||||
import ReactGridLayout from "react-grid-layout";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import SingleValueWidget from "./Widgets/SingleValueWidget";
|
||||
import { AutoSizer } from "react-virtualized";
|
||||
import LinearGraphWidget from "./Widgets/LinearGraphWidget";
|
||||
import {
|
||||
IBarChartConfiguration,
|
||||
IDataSRep,
|
||||
ILinearGraphConfiguration,
|
||||
IPieChartConfiguration,
|
||||
} from "./Widgets/types";
|
||||
import BarChartWidget from "./Widgets/BarChartWidget";
|
||||
import PieChartWidget from "./Widgets/PieChartWidget";
|
||||
import SingleRepWidget from "./Widgets/SingleRepWidget";
|
||||
import DateTimePickerWrapper from "../../Common/FormComponents/DateTimePickerWrapper/DateTimePickerWrapper";
|
||||
import { IDashboardPanel, widgetType } from "./types";
|
||||
import api from "../../../../common/api";
|
||||
import {
|
||||
getDashboardDistribution,
|
||||
getWidgetsWithValue,
|
||||
panelsConfiguration,
|
||||
saveDashboardDistribution,
|
||||
} from "./utils";
|
||||
import { Button } from "@material-ui/core";
|
||||
import ErrorBlock from "../../../shared/ErrorBlock";
|
||||
|
||||
interface IPrDashboard {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
widgetsContainer: {
|
||||
height: "calc(100vh - 250px)",
|
||||
},
|
||||
...actionsTray,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
const PrDashboard = ({ classes }: IPrDashboard) => {
|
||||
const [timeStart, setTimeStart] = useState<any>(null);
|
||||
const [timeEnd, setTimeEnd] = useState<any>(null);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [panelInformation, setPanelInformation] = useState<IDashboardPanel[]>(
|
||||
panelsConfiguration
|
||||
);
|
||||
const [error, setError] = useState<string>("");
|
||||
|
||||
const minHeight = 600;
|
||||
|
||||
const panels = useMemo(() => {
|
||||
const componentToUse = (value: IDashboardPanel) => {
|
||||
switch (value.type) {
|
||||
case widgetType.singleValue:
|
||||
return (
|
||||
<SingleValueWidget
|
||||
title={value.title}
|
||||
data={value.data as string}
|
||||
/>
|
||||
);
|
||||
case widgetType.pieChart:
|
||||
return (
|
||||
<PieChartWidget
|
||||
title={value.title}
|
||||
dataInner={value.data as object[]}
|
||||
dataOuter={(value.dataOuter as object[]) || null}
|
||||
pieChartConfiguration={
|
||||
value.widgetConfiguration as IPieChartConfiguration
|
||||
}
|
||||
middleLabel={value.innerLabel}
|
||||
/>
|
||||
);
|
||||
case widgetType.linearGraph:
|
||||
return (
|
||||
<LinearGraphWidget
|
||||
title={value.title}
|
||||
data={value.data as object[]}
|
||||
linearConfiguration={
|
||||
value.widgetConfiguration as ILinearGraphConfiguration[]
|
||||
}
|
||||
hideYAxis={value.disableYAxis}
|
||||
xAxisFormatter={value.xAxisFormatter}
|
||||
yAxisFormatter={value.yAxisFormatter}
|
||||
/>
|
||||
);
|
||||
case widgetType.barChart:
|
||||
return (
|
||||
<BarChartWidget
|
||||
title={value.title}
|
||||
data={value.data as object[]}
|
||||
barChartConfiguration={
|
||||
value.widgetConfiguration as IBarChartConfiguration[]
|
||||
}
|
||||
/>
|
||||
);
|
||||
case widgetType.singleRep:
|
||||
const fillColor = value.fillColor ? value.fillColor : value.color;
|
||||
return (
|
||||
<SingleRepWidget
|
||||
title={value.title}
|
||||
data={value.data as IDataSRep[]}
|
||||
label={value.innerLabel as string}
|
||||
color={value.color as string}
|
||||
fillColor={fillColor as string}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return panelInformation.map((val, idx) => {
|
||||
return <div key={val.layoutIdentifier}>{componentToUse(val)}</div>;
|
||||
});
|
||||
}, [panelInformation]);
|
||||
|
||||
const fetchUsage = useCallback(() => {
|
||||
let stepCalc = 15;
|
||||
|
||||
if (timeStart !== null && timeEnd !== null) {
|
||||
const secondsInPeriod = timeEnd.unix() - timeStart.unix();
|
||||
const periods = secondsInPeriod / 60;
|
||||
|
||||
stepCalc = periods < 1 ? 15 : periods;
|
||||
}
|
||||
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/admin/info?step=${stepCalc}&${
|
||||
timeStart !== null ? `&start=${timeStart.unix()}` : ""
|
||||
}${timeStart !== null && timeEnd !== null ? "&" : ""}${
|
||||
timeEnd !== null ? `end=${timeEnd.unix()}` : ""
|
||||
}`
|
||||
)
|
||||
.then((res: any) => {
|
||||
if (res.widgets) {
|
||||
const widgetsWithValue = getWidgetsWithValue(res.widgets);
|
||||
setPanelInformation(widgetsWithValue);
|
||||
setError("");
|
||||
} else {
|
||||
setError(
|
||||
"Widget information could not be retrieved at this time. Please try again"
|
||||
);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
setError(err);
|
||||
setLoading(false);
|
||||
});
|
||||
}, [timeStart, timeEnd]);
|
||||
|
||||
const triggerLoad = () => {
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
fetchUsage();
|
||||
}
|
||||
}, [loading, fetchUsage]);
|
||||
|
||||
const dashboardDistr = getDashboardDistribution();
|
||||
|
||||
return (
|
||||
<Grid container className={classes.container}>
|
||||
<Grid item xs={12}>
|
||||
{error !== "" && (
|
||||
<Fragment>
|
||||
<ErrorBlock errorMessage={error} withBreak={false} />
|
||||
<br />
|
||||
</Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={`${classes.actionsTray} ${classes.timeContainers}`}
|
||||
>
|
||||
<span className={classes.label}>Start Time</span>
|
||||
<DateTimePickerWrapper value={timeStart} onChange={setTimeStart} />
|
||||
<span className={classes.label}>End Time</span>
|
||||
<DateTimePickerWrapper value={timeEnd} onChange={setTimeEnd} />
|
||||
<Button
|
||||
type="button"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={triggerLoad}
|
||||
>
|
||||
Get Information
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.widgetsContainer}>
|
||||
<AutoSizer>
|
||||
{({ width, height }: any) => {
|
||||
const hpanel = height < minHeight ? minHeight : height;
|
||||
return (
|
||||
<ReactGridLayout
|
||||
width={width}
|
||||
cols={8}
|
||||
containerPadding={[10, 10]}
|
||||
onLayoutChange={saveDashboardDistribution}
|
||||
layout={dashboardDistr}
|
||||
rowHeight={hpanel / 6}
|
||||
>
|
||||
{panels}
|
||||
</ReactGridLayout>
|
||||
);
|
||||
}}
|
||||
</AutoSizer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(PrDashboard);
|
||||
@@ -0,0 +1,106 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
Bar,
|
||||
BarChart,
|
||||
ResponsiveContainer,
|
||||
XAxis,
|
||||
Text,
|
||||
YAxis,
|
||||
Tooltip,
|
||||
} from "recharts";
|
||||
import { IBarChartConfiguration } from "./types";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary";
|
||||
import BarChartTooltip from "./tooltips/BarChartTooltip";
|
||||
|
||||
interface IBarChartWidget {
|
||||
classes: any;
|
||||
title: string;
|
||||
barChartConfiguration: IBarChartConfiguration[];
|
||||
data: object[];
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...widgetCommon,
|
||||
});
|
||||
|
||||
const CustomizedAxisTick = ({ x, y, payload }: any) => {
|
||||
return (
|
||||
<g transform={`translate(${x},${y})`}>
|
||||
<Text
|
||||
width={50}
|
||||
fontSize={"63%"}
|
||||
textAnchor="end"
|
||||
verticalAnchor="middle"
|
||||
angle={0}
|
||||
fill="#333"
|
||||
>
|
||||
{payload.value}
|
||||
</Text>
|
||||
</g>
|
||||
);
|
||||
};
|
||||
|
||||
const BarChartWidget = ({
|
||||
classes,
|
||||
title,
|
||||
barChartConfiguration,
|
||||
data,
|
||||
}: IBarChartWidget) => {
|
||||
return (
|
||||
<div className={classes.singleValueContainer}>
|
||||
<div className={classes.titleContainer}>{title}</div>
|
||||
<div className={classes.contentContainer}>
|
||||
<ResponsiveContainer>
|
||||
<BarChart data={data} layout={"vertical"} barCategoryGap={1}>
|
||||
<XAxis type="number" hide />
|
||||
<YAxis
|
||||
dataKey="name"
|
||||
type="category"
|
||||
interval={0}
|
||||
tick={<CustomizedAxisTick />}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
width={150}
|
||||
/>
|
||||
{barChartConfiguration.map((bar) => (
|
||||
<Bar
|
||||
key={`bar-${bar.dataKey}`}
|
||||
dataKey={bar.dataKey}
|
||||
fill={bar.color}
|
||||
background={bar.background}
|
||||
/>
|
||||
))}
|
||||
<Tooltip
|
||||
cursor={{ fill: "rgba(255, 255, 255, 0.3)" }}
|
||||
content={
|
||||
<BarChartTooltip
|
||||
barChartConfiguration={barChartConfiguration}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(BarChartWidget);
|
||||
@@ -0,0 +1,157 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
Area,
|
||||
AreaChart,
|
||||
CartesianGrid,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from "recharts";
|
||||
import { ILinearGraphConfiguration } from "./types";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary";
|
||||
import LineChartTooltip from "./tooltips/LineChartTooltip";
|
||||
|
||||
interface ILinearGraphWidget {
|
||||
classes: any;
|
||||
title: string;
|
||||
linearConfiguration: ILinearGraphConfiguration[];
|
||||
data: object[];
|
||||
hideYAxis?: boolean;
|
||||
yAxisFormatter?: (item: string) => string;
|
||||
xAxisFormatter?: (item: string) => string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...widgetCommon,
|
||||
containerElements: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
height: "calc(100% - 18px)",
|
||||
},
|
||||
chartCont: {
|
||||
position: "relative",
|
||||
flexGrow: 1,
|
||||
minHeight: "65%",
|
||||
height: 1,
|
||||
},
|
||||
legendChart: {
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
flex: "0 1 auto",
|
||||
maxHeight: "35%",
|
||||
margin: 0,
|
||||
overflowY: "auto",
|
||||
position: "relative",
|
||||
textAlign: "center",
|
||||
},
|
||||
});
|
||||
|
||||
const LinearGraphWidget = ({
|
||||
classes,
|
||||
title,
|
||||
linearConfiguration,
|
||||
data,
|
||||
hideYAxis = false,
|
||||
yAxisFormatter = (item: string) => item,
|
||||
xAxisFormatter = (item: string) => item,
|
||||
}: ILinearGraphWidget) => {
|
||||
return (
|
||||
<div className={classes.singleValueContainer}>
|
||||
<div className={classes.titleContainer}>{title}</div>
|
||||
<div className={classes.containerElements}>
|
||||
<div className={classes.chartCont}>
|
||||
<ResponsiveContainer>
|
||||
<AreaChart
|
||||
data={data}
|
||||
margin={{
|
||||
top: 5,
|
||||
right: 20,
|
||||
left: hideYAxis ? 20 : 5,
|
||||
bottom: 0,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid
|
||||
strokeDasharray="3 3"
|
||||
strokeWidth={1}
|
||||
strokeOpacity={0.5}
|
||||
/>
|
||||
<XAxis
|
||||
dataKey="name"
|
||||
tickFormatter={xAxisFormatter}
|
||||
interval={5}
|
||||
tick={{ fontSize: "70%" }}
|
||||
tickCount={10}
|
||||
/>
|
||||
<YAxis
|
||||
domain={[0, (dataMax) => dataMax * 4]}
|
||||
hide={hideYAxis}
|
||||
tickFormatter={yAxisFormatter}
|
||||
tick={{ fontSize: "70%" }}
|
||||
/>
|
||||
{linearConfiguration.map((section, index) => {
|
||||
return (
|
||||
<Area
|
||||
key={`area-${section.dataKey}-${index.toString()}`}
|
||||
type="monotone"
|
||||
dataKey={section.dataKey}
|
||||
stroke={section.lineColor}
|
||||
fill={section.fillColor}
|
||||
fillOpacity={0.3}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<Tooltip
|
||||
content={
|
||||
<LineChartTooltip
|
||||
linearConfiguration={linearConfiguration}
|
||||
yAxisFormatter={yAxisFormatter}
|
||||
/>
|
||||
}
|
||||
wrapperStyle={{
|
||||
zIndex: 5000,
|
||||
}}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
<div className={classes.legendChart}>
|
||||
{linearConfiguration.map((section, index) => {
|
||||
return (
|
||||
<div
|
||||
className={classes.singleLegendContainer}
|
||||
key={`legend-${section.keyLabel}-${index.toString()}`}
|
||||
>
|
||||
<div
|
||||
className={classes.colorContainer}
|
||||
style={{ backgroundColor: section.lineColor }}
|
||||
/>
|
||||
<div className={classes.legendLabel}>{section.keyLabel}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(LinearGraphWidget);
|
||||
@@ -0,0 +1,156 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
import React from "react";
|
||||
import get from "lodash/get";
|
||||
import { Cell, Pie, PieChart, ResponsiveContainer } from "recharts";
|
||||
import { IPieChartConfiguration } from "./types";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary";
|
||||
|
||||
interface IPieChartWidget {
|
||||
classes: any;
|
||||
title: string;
|
||||
pieChartConfiguration: IPieChartConfiguration;
|
||||
dataInner: object[];
|
||||
dataOuter?: object[];
|
||||
middleLabel?: string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...widgetCommon,
|
||||
});
|
||||
|
||||
const PieChartWidget = ({
|
||||
classes,
|
||||
title,
|
||||
pieChartConfiguration,
|
||||
dataInner,
|
||||
dataOuter,
|
||||
middleLabel = "",
|
||||
}: IPieChartWidget) => {
|
||||
const innerColors = get(pieChartConfiguration, "innerChart.colorList", []);
|
||||
const outerColors = get(pieChartConfiguration, "outerChart.colorList", []);
|
||||
|
||||
return (
|
||||
<div className={classes.singleValueContainer}>
|
||||
<div className={classes.titleContainer}>{title}</div>
|
||||
<div className={classes.contentContainer}>
|
||||
<ResponsiveContainer>
|
||||
<PieChart margin={{ top: 5, bottom: 5 }}>
|
||||
{dataOuter && (
|
||||
<Pie
|
||||
data={dataOuter}
|
||||
cx={"50%"}
|
||||
cy={"50%"}
|
||||
dataKey="value"
|
||||
innerRadius={get(
|
||||
pieChartConfiguration,
|
||||
"outerChart.innerRadius",
|
||||
0
|
||||
)}
|
||||
outerRadius={get(
|
||||
pieChartConfiguration,
|
||||
"outerChart.outerRadius",
|
||||
"80%"
|
||||
)}
|
||||
startAngle={get(
|
||||
pieChartConfiguration,
|
||||
"outerChart.startAngle",
|
||||
0
|
||||
)}
|
||||
endAngle={get(
|
||||
pieChartConfiguration,
|
||||
"outerChart.endAngle",
|
||||
360
|
||||
)}
|
||||
fill="#201763"
|
||||
>
|
||||
{dataOuter.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cellOuter-${index}`}
|
||||
fill={
|
||||
typeof outerColors[index] == "undefined"
|
||||
? "#393939"
|
||||
: outerColors[index]
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
)}
|
||||
{dataInner && (
|
||||
<Pie
|
||||
data={dataInner}
|
||||
dataKey="value"
|
||||
cx={"50%"}
|
||||
cy={"50%"}
|
||||
innerRadius={get(
|
||||
pieChartConfiguration,
|
||||
"innerChart.innerRadius",
|
||||
0
|
||||
)}
|
||||
outerRadius={get(
|
||||
pieChartConfiguration,
|
||||
"innerChart.outerRadius",
|
||||
"80%"
|
||||
)}
|
||||
startAngle={get(
|
||||
pieChartConfiguration,
|
||||
"innerChart.startAngle",
|
||||
0
|
||||
)}
|
||||
endAngle={get(
|
||||
pieChartConfiguration,
|
||||
"innerChart.endAngle",
|
||||
360
|
||||
)}
|
||||
fill="#201763"
|
||||
>
|
||||
{dataInner.map((entry, index) => {
|
||||
return (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={
|
||||
typeof innerColors[index] == "undefined"
|
||||
? "#393939"
|
||||
: innerColors[index]
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Pie>
|
||||
)}
|
||||
{middleLabel && (
|
||||
<text
|
||||
x={"50%"}
|
||||
y={"50%"}
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
fontWeight={600}
|
||||
fontSize={14}
|
||||
>
|
||||
{middleLabel}
|
||||
</text>
|
||||
)}
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(PieChartWidget);
|
||||
@@ -0,0 +1,77 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
import React from "react";
|
||||
import { Area, AreaChart, ResponsiveContainer, YAxis } from "recharts";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary";
|
||||
import { IDataSRep } from "./types";
|
||||
|
||||
interface ISingleRepWidget {
|
||||
classes: any;
|
||||
title: string;
|
||||
data: IDataSRep[];
|
||||
color: string;
|
||||
fillColor: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...widgetCommon,
|
||||
});
|
||||
|
||||
const SingleRepWidget = ({
|
||||
classes,
|
||||
title,
|
||||
data,
|
||||
color,
|
||||
fillColor,
|
||||
label,
|
||||
}: ISingleRepWidget) => {
|
||||
return (
|
||||
<div className={classes.singleValueContainer}>
|
||||
<div className={classes.titleContainer}>{title}</div>
|
||||
<div className={classes.contentContainer}>
|
||||
<ResponsiveContainer>
|
||||
<AreaChart data={data}>
|
||||
<YAxis domain={[0, (dataMax) => dataMax * 2]} hide={true} />
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey={"value"}
|
||||
stroke={color}
|
||||
fill={fillColor}
|
||||
fillOpacity={1}
|
||||
/>
|
||||
<text
|
||||
x={"50%"}
|
||||
y={"50%"}
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
fontWeight={600}
|
||||
fontSize={18}
|
||||
fill={color}
|
||||
>
|
||||
{label}
|
||||
</text>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(SingleRepWidget);
|
||||
@@ -0,0 +1,48 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
import React from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary";
|
||||
|
||||
interface ISingleValueWidget {
|
||||
title: string;
|
||||
data: string;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...widgetCommon,
|
||||
contentContainer: {
|
||||
...widgetCommon.contentContainer,
|
||||
fontWeight: 700,
|
||||
color: "#072045",
|
||||
fontSize: 18,
|
||||
textAlign: "center" as const,
|
||||
},
|
||||
});
|
||||
|
||||
const SingleValueWidget = ({ title, data, classes }: ISingleValueWidget) => {
|
||||
return (
|
||||
<div className={classes.singleValueContainer}>
|
||||
<div className={classes.titleContainer}>{title}</div>
|
||||
<div className={classes.contentContainer}>{data}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(SingleValueWidget);
|
||||
@@ -0,0 +1,63 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
import React from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { tooltipCommon } from "../../../../Common/FormComponents/common/styleLibrary";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...tooltipCommon,
|
||||
});
|
||||
|
||||
const BarChartTooltip = ({
|
||||
active,
|
||||
payload,
|
||||
label,
|
||||
barChartConfiguration,
|
||||
classes,
|
||||
}: any) => {
|
||||
if (active) {
|
||||
return (
|
||||
<div className={classes.customTooltip}>
|
||||
<div className={classes.timeStampTitle}>{label}</div>
|
||||
{payload &&
|
||||
payload.map((pl: any, index: number) => {
|
||||
return (
|
||||
<div
|
||||
className={classes.labelContainer}
|
||||
key={`pltiem-${index}-${label}`}
|
||||
>
|
||||
<div
|
||||
className={classes.labelColor}
|
||||
style={{
|
||||
backgroundColor: barChartConfiguration[index].color,
|
||||
}}
|
||||
/>
|
||||
<div className={classes.itemValue}>
|
||||
<span className={classes.valueContainer}>{pl.value}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default withStyles(styles)(BarChartTooltip);
|
||||
@@ -0,0 +1,70 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
import React from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { getTimeFromTimestamp } from "../../../../../../common/utils";
|
||||
import { tooltipCommon } from "../../../../Common/FormComponents/common/styleLibrary";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...tooltipCommon,
|
||||
});
|
||||
|
||||
const LineChartTooltip = ({
|
||||
active,
|
||||
payload,
|
||||
label,
|
||||
linearConfiguration,
|
||||
yAxisFormatter,
|
||||
classes,
|
||||
}: any) => {
|
||||
if (active) {
|
||||
return (
|
||||
<div className={classes.customTooltip}>
|
||||
<div className={classes.timeStampTitle}>
|
||||
{getTimeFromTimestamp(label, true)}
|
||||
</div>
|
||||
{payload &&
|
||||
payload.map((pl: any, index: number) => {
|
||||
return (
|
||||
<div
|
||||
className={classes.labelContainer}
|
||||
key={`lbPl-${index}-${linearConfiguration[index].keyLabel}`}
|
||||
>
|
||||
<div
|
||||
className={classes.labelColor}
|
||||
style={{
|
||||
backgroundColor: linearConfiguration[index].lineColor,
|
||||
}}
|
||||
/>
|
||||
<div className={classes.itemValue}>
|
||||
<span className={classes.valueContainer}>
|
||||
{linearConfiguration[index].keyLabel}:{" "}
|
||||
{yAxisFormatter(pl.value)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default withStyles(styles)(LineChartTooltip);
|
||||
@@ -0,0 +1,45 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
export interface ILinearGraphConfiguration {
|
||||
dataKey: string;
|
||||
keyLabel: string;
|
||||
lineColor: string;
|
||||
fillColor: string;
|
||||
}
|
||||
|
||||
export interface IBarChartConfiguration {
|
||||
dataKey: string;
|
||||
color: string;
|
||||
background?: object;
|
||||
}
|
||||
|
||||
export interface IPieChartConfiguration {
|
||||
innerChart: ISinglePieConfiguration;
|
||||
outerChart?: ISinglePieConfiguration;
|
||||
}
|
||||
|
||||
export interface ISinglePieConfiguration {
|
||||
colorList: string[];
|
||||
startAngle?: number;
|
||||
endAngle?: number;
|
||||
innerRadius?: number | string;
|
||||
outerRadius?: number | string;
|
||||
}
|
||||
|
||||
export interface IDataSRep {
|
||||
value: number;
|
||||
}
|
||||
49
portal-ui/src/screens/Console/Dashboard/Prometheus/types.ts
Normal file
49
portal-ui/src/screens/Console/Dashboard/Prometheus/types.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
import {
|
||||
IBarChartConfiguration,
|
||||
IDataSRep,
|
||||
ILinearGraphConfiguration,
|
||||
IPieChartConfiguration,
|
||||
} from "./Widgets/types";
|
||||
|
||||
export enum widgetType {
|
||||
singleValue = "singleValue",
|
||||
linearGraph = "linearGraph",
|
||||
barChart = "barChart",
|
||||
pieChart = "pieChart",
|
||||
singleRep = "singleRep",
|
||||
}
|
||||
|
||||
export interface IDashboardPanel {
|
||||
title: string;
|
||||
data: string | object[] | IDataSRep[];
|
||||
dataOuter?: string | object[];
|
||||
type: widgetType;
|
||||
layoutIdentifier: string;
|
||||
widgetConfiguration?:
|
||||
| ILinearGraphConfiguration[]
|
||||
| IBarChartConfiguration[]
|
||||
| IPieChartConfiguration;
|
||||
color?: string;
|
||||
fillColor?: string;
|
||||
innerLabel?: string;
|
||||
labelDisplayFunction?: (value: string) => any;
|
||||
disableYAxis?: boolean;
|
||||
xAxisFormatter?: (item: string) => string;
|
||||
yAxisFormatter?: (item: string) => string;
|
||||
}
|
||||
605
portal-ui/src/screens/Console/Dashboard/Prometheus/utils.ts
Normal file
605
portal-ui/src/screens/Console/Dashboard/Prometheus/utils.ts
Normal file
@@ -0,0 +1,605 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
import get from "lodash/get";
|
||||
import { Layout } from "react-grid-layout";
|
||||
import { IDashboardPanel, widgetType } from "./types";
|
||||
import {
|
||||
getTimeFromTimestamp,
|
||||
niceBytes,
|
||||
niceDays,
|
||||
textToRGBColor,
|
||||
} from "../../../../common/utils";
|
||||
|
||||
const dLocalStorageV = "dashboardConfig";
|
||||
|
||||
export const defaultWidgetsLayout: Layout[] = [
|
||||
{ w: 1, h: 2, x: 0, y: 0, i: "panel-0", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 1, y: 0, i: "panel-1", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 1, y: 1, i: "panel-2", moved: false, static: false },
|
||||
{ w: 1, h: 2, x: 2, y: 0, i: "panel-3", moved: false, static: false },
|
||||
{ w: 2, h: 2, x: 3, y: 0, i: "panel-4", moved: false, static: false },
|
||||
{ w: 3, h: 2, x: 5, y: 0, i: "panel-5", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 0, y: 2, i: "panel-6", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 0, y: 3, i: "panel-7", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 1, y: 2, i: "panel-8", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 1, y: 3, i: "panel-9", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 2, y: 2, i: "panel-10", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 2, y: 3, i: "panel-11", moved: false, static: false },
|
||||
{ w: 4, h: 2, x: 3, y: 2, i: "panel-12", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 7, y: 2, i: "panel-13", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 7, y: 3, i: "panel-14", moved: false, static: false },
|
||||
{ w: 8, h: 2, x: 0, y: 4, i: "panel-15", moved: false, static: false },
|
||||
{ w: 4, h: 2, x: 0, y: 5, i: "panel-16", moved: false, static: false },
|
||||
{ w: 4, h: 2, x: 5, y: 5, i: "panel-17", moved: false, static: false },
|
||||
{ w: 8, h: 2, x: 0, y: 7, i: "panel-18", moved: false, static: false },
|
||||
{ w: 4, h: 2, x: 0, y: 9, i: "panel-19", moved: false, static: false },
|
||||
{ w: 4, h: 2, x: 5, y: 9, i: "panel-20", moved: false, static: false },
|
||||
];
|
||||
|
||||
const colorsMain = [
|
||||
"#6992B7",
|
||||
"#E2AD17",
|
||||
"#22B573",
|
||||
"#F7655E",
|
||||
"#0071BC",
|
||||
"#F9E6C5",
|
||||
"#A6E8C4",
|
||||
"#F4CECE",
|
||||
"#ADD5E0",
|
||||
];
|
||||
|
||||
export const panelsConfiguration: IDashboardPanel[] = [
|
||||
{
|
||||
title: "Uptime",
|
||||
data: "N/A",
|
||||
type: widgetType.singleValue,
|
||||
layoutIdentifier: "panel-0",
|
||||
labelDisplayFunction: niceDays,
|
||||
},
|
||||
{
|
||||
title: "Total Online disks",
|
||||
data: "N/A",
|
||||
type: widgetType.singleValue,
|
||||
layoutIdentifier: "panel-1",
|
||||
},
|
||||
{
|
||||
title: "Total Offline disks",
|
||||
data: "N/A",
|
||||
type: widgetType.singleValue,
|
||||
layoutIdentifier: "panel-2",
|
||||
},
|
||||
{
|
||||
title: "Total Data",
|
||||
data: [],
|
||||
dataOuter: [{ name: "outer", value: 100 }],
|
||||
widgetConfiguration: {
|
||||
outerChart: {
|
||||
colorList: ["#9c9c9c"],
|
||||
innerRadius: 51,
|
||||
outerRadius: 54,
|
||||
startAngle: -15,
|
||||
endAngle: 195,
|
||||
},
|
||||
innerChart: {
|
||||
colorList: colorsMain,
|
||||
innerRadius: 35,
|
||||
outerRadius: 50,
|
||||
startAngle: -15,
|
||||
endAngle: 195,
|
||||
},
|
||||
},
|
||||
type: widgetType.pieChart,
|
||||
layoutIdentifier: "panel-3",
|
||||
innerLabel: "N/A",
|
||||
labelDisplayFunction: niceBytes,
|
||||
},
|
||||
{
|
||||
title: "Data Growth",
|
||||
data: [],
|
||||
widgetConfiguration: [
|
||||
{
|
||||
dataKey: "",
|
||||
keyLabel: "",
|
||||
lineColor: "#000",
|
||||
fillColor: "#000",
|
||||
},
|
||||
],
|
||||
type: widgetType.linearGraph,
|
||||
layoutIdentifier: "panel-4",
|
||||
yAxisFormatter: niceBytes,
|
||||
xAxisFormatter: getTimeFromTimestamp,
|
||||
},
|
||||
{
|
||||
title: "Object size distribution",
|
||||
data: [],
|
||||
widgetConfiguration: [
|
||||
{
|
||||
dataKey: "a",
|
||||
color: colorsMain[0],
|
||||
background: {
|
||||
fill: "rgba(0,0,0,0.1)",
|
||||
},
|
||||
},
|
||||
],
|
||||
type: widgetType.barChart,
|
||||
layoutIdentifier: "panel-5",
|
||||
},
|
||||
{
|
||||
title: "Total Online Servers",
|
||||
data: "N/A",
|
||||
type: widgetType.singleValue,
|
||||
layoutIdentifier: "panel-6",
|
||||
},
|
||||
{
|
||||
title: "Total Offline Servers",
|
||||
data: "N/A",
|
||||
type: widgetType.singleValue,
|
||||
layoutIdentifier: "panel-7",
|
||||
},
|
||||
{
|
||||
title: "Total S3 Traffic Inbound",
|
||||
data: [],
|
||||
innerLabel: "N/A",
|
||||
type: widgetType.singleRep,
|
||||
layoutIdentifier: "panel-8",
|
||||
color: "#22B573",
|
||||
fillColor: "#A6E8C4",
|
||||
labelDisplayFunction: niceBytes,
|
||||
},
|
||||
{
|
||||
title: "Total S3 Traffic Outbound",
|
||||
data: [],
|
||||
innerLabel: "N/A",
|
||||
type: widgetType.singleRep,
|
||||
layoutIdentifier: "panel-9",
|
||||
color: "#22B573",
|
||||
fillColor: "#A6E8C4",
|
||||
labelDisplayFunction: niceBytes,
|
||||
},
|
||||
{
|
||||
title: "Number of Buckets",
|
||||
data: [],
|
||||
innerLabel: "N/A",
|
||||
type: widgetType.singleRep,
|
||||
color: "#0071BC",
|
||||
fillColor: "#ADD5E0",
|
||||
layoutIdentifier: "panel-10",
|
||||
},
|
||||
{
|
||||
title: "Number of Objects",
|
||||
data: [],
|
||||
innerLabel: "N/A",
|
||||
type: widgetType.singleRep,
|
||||
color: "#0071BC",
|
||||
fillColor: "#ADD5E0",
|
||||
layoutIdentifier: "panel-11",
|
||||
},
|
||||
{
|
||||
title: "S3 API Request & Error Rate",
|
||||
data: [],
|
||||
widgetConfiguration: [
|
||||
{
|
||||
dataKey: "",
|
||||
keyLabel: "",
|
||||
lineColor: "#000",
|
||||
fillColor: "#000",
|
||||
},
|
||||
],
|
||||
type: widgetType.linearGraph,
|
||||
layoutIdentifier: "panel-12",
|
||||
disableYAxis: true,
|
||||
xAxisFormatter: getTimeFromTimestamp,
|
||||
},
|
||||
{
|
||||
title: "Total Open FDs",
|
||||
data: [],
|
||||
innerLabel: "N/A",
|
||||
type: widgetType.singleRep,
|
||||
layoutIdentifier: "panel-13",
|
||||
color: "#F7655E",
|
||||
fillColor: "#F4CECE",
|
||||
},
|
||||
{
|
||||
title: "Total Goroutines",
|
||||
data: [],
|
||||
innerLabel: "N/A",
|
||||
type: widgetType.singleRep,
|
||||
layoutIdentifier: "panel-14",
|
||||
color: "#F7655E",
|
||||
fillColor: "#F4CECE",
|
||||
},
|
||||
{
|
||||
title: "S3 API Data Transfer",
|
||||
data: [],
|
||||
widgetConfiguration: [
|
||||
{
|
||||
dataKey: "",
|
||||
keyLabel: "",
|
||||
lineColor: "#000",
|
||||
fillColor: "#000",
|
||||
},
|
||||
],
|
||||
type: widgetType.linearGraph,
|
||||
layoutIdentifier: "panel-15",
|
||||
disableYAxis: true,
|
||||
xAxisFormatter: getTimeFromTimestamp,
|
||||
},
|
||||
{
|
||||
title: "Total S3 API Data Transfer",
|
||||
data: [],
|
||||
widgetConfiguration: [
|
||||
{
|
||||
dataKey: "",
|
||||
keyLabel: "",
|
||||
lineColor: "#000",
|
||||
fillColor: "#000",
|
||||
},
|
||||
],
|
||||
type: widgetType.linearGraph,
|
||||
layoutIdentifier: "panel-16",
|
||||
yAxisFormatter: niceBytes,
|
||||
xAxisFormatter: getTimeFromTimestamp,
|
||||
},
|
||||
{
|
||||
title: "Active S3 Requests",
|
||||
data: [],
|
||||
widgetConfiguration: [
|
||||
{
|
||||
dataKey: "",
|
||||
keyLabel: "",
|
||||
lineColor: "#000",
|
||||
fillColor: "#000",
|
||||
},
|
||||
],
|
||||
type: widgetType.linearGraph,
|
||||
layoutIdentifier: "panel-17",
|
||||
xAxisFormatter: getTimeFromTimestamp,
|
||||
},
|
||||
{
|
||||
title: "Internode Data Transfer",
|
||||
data: [],
|
||||
widgetConfiguration: [
|
||||
{
|
||||
dataKey: "",
|
||||
keyLabel: "",
|
||||
lineColor: "#000",
|
||||
fillColor: "#000",
|
||||
},
|
||||
],
|
||||
type: widgetType.linearGraph,
|
||||
layoutIdentifier: "panel-18",
|
||||
yAxisFormatter: niceBytes,
|
||||
xAxisFormatter: getTimeFromTimestamp,
|
||||
},
|
||||
{
|
||||
title: "Online Disks",
|
||||
data: [],
|
||||
widgetConfiguration: [
|
||||
{
|
||||
dataKey: "",
|
||||
keyLabel: "",
|
||||
lineColor: "#000",
|
||||
fillColor: "#000",
|
||||
},
|
||||
],
|
||||
type: widgetType.linearGraph,
|
||||
layoutIdentifier: "panel-19",
|
||||
disableYAxis: true,
|
||||
xAxisFormatter: getTimeFromTimestamp,
|
||||
},
|
||||
{
|
||||
title: "Disk Usage",
|
||||
data: [],
|
||||
widgetConfiguration: [
|
||||
{
|
||||
dataKey: "",
|
||||
keyLabel: "",
|
||||
lineColor: "#000",
|
||||
fillColor: "#000",
|
||||
},
|
||||
],
|
||||
type: widgetType.linearGraph,
|
||||
layoutIdentifier: "panel-20",
|
||||
yAxisFormatter: niceBytes,
|
||||
xAxisFormatter: getTimeFromTimestamp,
|
||||
},
|
||||
];
|
||||
|
||||
const calculateMainValue = (elements: any[], metricCalc: string) => {
|
||||
switch (metricCalc) {
|
||||
case "mean":
|
||||
const sumValues = elements.reduce((accumulator, currValue) => {
|
||||
return accumulator + parseFloat(currValue[1]);
|
||||
}, 0);
|
||||
|
||||
const mean = Math.floor(sumValues / elements.length);
|
||||
|
||||
return ["", mean.toString()];
|
||||
default:
|
||||
const sortResult = elements.sort(
|
||||
(value1: any[], value2: any[]) => value1[0] - value2[0]
|
||||
);
|
||||
|
||||
return sortResult[sortResult.length - 1];
|
||||
}
|
||||
};
|
||||
|
||||
const constructLabelNames = (metrics: any, legendFormat: string) => {
|
||||
const keysToReplace = Object.keys(metrics);
|
||||
const expToReplace = new RegExp(`{{(${keysToReplace.join("|")})}}`, "g");
|
||||
|
||||
const replacedLegend = legendFormat.replace(expToReplace, (matchItem) => {
|
||||
const nwMatchItem = matchItem.replace(/({{|}})/g, "");
|
||||
return metrics[nwMatchItem];
|
||||
});
|
||||
|
||||
// In case not all the legends were replaced, we remove the placeholders.
|
||||
return replacedLegend.replace(/{{(.*?)}}/g, "");
|
||||
};
|
||||
|
||||
export const getWidgetsWithValue = (payload: any[]) => {
|
||||
return panelsConfiguration.map((panelItem) => {
|
||||
const payloadData = payload.find(
|
||||
(panelT) => panelT.title === panelItem.title
|
||||
);
|
||||
|
||||
if (!payloadData) {
|
||||
return panelItem;
|
||||
}
|
||||
|
||||
const typeOfPayload = payloadData.type;
|
||||
|
||||
switch (panelItem.type) {
|
||||
case widgetType.singleValue:
|
||||
if (typeOfPayload === "stat" || typeOfPayload === "singlestat") {
|
||||
// We sort values & get the last value
|
||||
const elements = get(payloadData, "targets[0].result[0].values", []);
|
||||
const metricCalc = get(
|
||||
payloadData,
|
||||
"options.reduceOptions.calcs[0]",
|
||||
"lastNotNull"
|
||||
);
|
||||
|
||||
const valueDisplay = calculateMainValue(elements, metricCalc);
|
||||
|
||||
const data = panelItem.labelDisplayFunction
|
||||
? panelItem.labelDisplayFunction(valueDisplay[1])
|
||||
: valueDisplay[1];
|
||||
|
||||
return {
|
||||
...panelItem,
|
||||
data,
|
||||
};
|
||||
}
|
||||
break;
|
||||
case widgetType.pieChart:
|
||||
if (typeOfPayload === "gauge") {
|
||||
const chartSeries = get(payloadData, "targets[0].result", []);
|
||||
const metricCalc = get(
|
||||
payloadData,
|
||||
"options.reduceOptions.calcs[0]",
|
||||
"lastNotNull"
|
||||
);
|
||||
|
||||
const totalValues = calculateMainValue(
|
||||
chartSeries[0].values,
|
||||
metricCalc
|
||||
);
|
||||
|
||||
const values = chartSeries.map((elementValue: any) => {
|
||||
const values = get(elementValue, "values", []);
|
||||
const metricKeyItem = Object.keys(elementValue.metric);
|
||||
|
||||
const sortResult = values.sort(
|
||||
(value1: any[], value2: any[]) => value1[0] - value2[0]
|
||||
);
|
||||
|
||||
const metricName = elementValue.metric[metricKeyItem[0]];
|
||||
const value = sortResult[sortResult.length - 1];
|
||||
return { name: metricName, value: parseInt(value) };
|
||||
});
|
||||
|
||||
const innerLabel = panelItem.labelDisplayFunction
|
||||
? panelItem.labelDisplayFunction(totalValues[1])
|
||||
: totalValues[1];
|
||||
|
||||
return {
|
||||
...panelItem,
|
||||
data: values,
|
||||
innerLabel,
|
||||
};
|
||||
}
|
||||
break;
|
||||
case widgetType.linearGraph:
|
||||
if (typeOfPayload === "graph") {
|
||||
let targets = get(payloadData, "targets", []);
|
||||
if (targets === null) {
|
||||
targets = [];
|
||||
}
|
||||
|
||||
const series: any[] = [];
|
||||
const plotValues: any[] = [];
|
||||
|
||||
targets.forEach(
|
||||
(
|
||||
targetMaster: { legendFormat: string; result: any[] },
|
||||
index: number
|
||||
) => {
|
||||
// Add a new serie to plot variables in case it is not from multiple values
|
||||
let results = get(targetMaster, "result", []);
|
||||
const legendFormat = targetMaster.legendFormat;
|
||||
if (results === null) {
|
||||
results = [];
|
||||
}
|
||||
|
||||
results.forEach((itemVals: { metric: object; values: any[] }) => {
|
||||
// Label Creation
|
||||
const labelName = constructLabelNames(
|
||||
itemVals.metric,
|
||||
legendFormat
|
||||
);
|
||||
const keyName = `key_${index}${labelName}`;
|
||||
|
||||
// series creation with recently created label
|
||||
series.push({
|
||||
dataKey: keyName,
|
||||
keyLabel: labelName,
|
||||
lineColor: "",
|
||||
fillColor: "",
|
||||
});
|
||||
|
||||
// we iterate over values and create elements
|
||||
let values = get(itemVals, "values", []);
|
||||
if (values === null) {
|
||||
values = [];
|
||||
}
|
||||
|
||||
values.forEach((valInfo: any[]) => {
|
||||
const itemIndex = plotValues.findIndex(
|
||||
(element) => element.name === valInfo[0]
|
||||
);
|
||||
|
||||
// Element not exists yet
|
||||
if (itemIndex === -1) {
|
||||
let itemToPush: any = { name: valInfo[0] };
|
||||
itemToPush[keyName] = valInfo[1];
|
||||
|
||||
plotValues.push(itemToPush);
|
||||
} else {
|
||||
plotValues[itemIndex][keyName] = valInfo[1];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const sortedSeries = series.sort((series1: any, series2: any) => {
|
||||
if (series1.keyLabel < series2.keyLabel) {
|
||||
return -1;
|
||||
}
|
||||
if (series1.keyLabel > series2.keyLabel) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
const seriesWithColors = sortedSeries.map(
|
||||
(serialC: any, index: number) => {
|
||||
return {
|
||||
...serialC,
|
||||
lineColor:
|
||||
colorsMain[index] || textToRGBColor(serialC.keyLabel),
|
||||
fillColor:
|
||||
colorsMain[index] || textToRGBColor(serialC.keyLabel),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const sortedVals = plotValues.sort(
|
||||
(value1: any, value2: any) => value1.name - value2.name
|
||||
);
|
||||
|
||||
return {
|
||||
...panelItem,
|
||||
widgetConfiguration: seriesWithColors,
|
||||
data: sortedVals,
|
||||
};
|
||||
}
|
||||
break;
|
||||
case widgetType.barChart:
|
||||
if (typeOfPayload === "bargauge") {
|
||||
const chartBars = get(payloadData, "targets[0].result", []);
|
||||
|
||||
const values = chartBars.map((elementValue: any) => {
|
||||
const metricKeyItem = Object.keys(elementValue.metric);
|
||||
|
||||
const metricName = elementValue.metric[metricKeyItem[0]];
|
||||
|
||||
const elements = get(elementValue, "values", []);
|
||||
|
||||
const sortResult = elements.sort(
|
||||
(value1: any[], value2: any[]) => value1[0] - value2[0]
|
||||
);
|
||||
const lastValue = sortResult[sortResult.length - 1];
|
||||
return { name: metricName, a: parseInt(lastValue[1]) };
|
||||
});
|
||||
|
||||
return {
|
||||
...panelItem,
|
||||
data: values,
|
||||
};
|
||||
}
|
||||
break;
|
||||
case widgetType.singleRep:
|
||||
if (typeOfPayload === "stat") {
|
||||
// We sort values & get the last value
|
||||
let elements = get(payloadData, "targets[0].result[0].values", []);
|
||||
if (elements === null) {
|
||||
elements = [];
|
||||
}
|
||||
const metricCalc = get(
|
||||
payloadData,
|
||||
"options.reduceOptions.calcs[0]",
|
||||
"lastNotNull"
|
||||
);
|
||||
|
||||
const valueDisplay = calculateMainValue(elements, metricCalc);
|
||||
|
||||
const sortResult = elements.sort(
|
||||
(value1: any[], value2: any[]) => value1[0] - value2[0]
|
||||
);
|
||||
|
||||
let valuesForBackground = [];
|
||||
|
||||
if (sortResult.length === 1) {
|
||||
valuesForBackground.push({ value: 0 });
|
||||
}
|
||||
|
||||
sortResult.forEach((eachVal: any) => {
|
||||
valuesForBackground.push({ value: parseInt(eachVal[1]) });
|
||||
});
|
||||
|
||||
const innerLabel = panelItem.labelDisplayFunction
|
||||
? panelItem.labelDisplayFunction(valueDisplay[1])
|
||||
: valueDisplay[1];
|
||||
|
||||
return {
|
||||
...panelItem,
|
||||
data: valuesForBackground,
|
||||
innerLabel,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return panelItem;
|
||||
});
|
||||
};
|
||||
|
||||
export const saveDashboardDistribution = (configuration: Layout[]) => {
|
||||
localStorage.setItem(dLocalStorageV, btoa(JSON.stringify(configuration)));
|
||||
};
|
||||
|
||||
export const getDashboardDistribution = () => {
|
||||
const storedConfiguration = localStorage.getItem(dLocalStorageV);
|
||||
|
||||
if (!storedConfiguration) {
|
||||
return defaultWidgetsLayout;
|
||||
}
|
||||
|
||||
return JSON.parse(atob(storedConfiguration));
|
||||
};
|
||||
@@ -18,4 +18,5 @@ export interface Usage {
|
||||
usage: number;
|
||||
buckets: number;
|
||||
objects: number;
|
||||
widgets?: any;
|
||||
}
|
||||
|
||||
@@ -198,7 +198,6 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
console.log("operatorMode", operatorMode);
|
||||
return (
|
||||
<React.Fragment>
|
||||
{licenseInfo ? (
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export interface LicenseInfo {
|
||||
account_id: number,
|
||||
account_id: number;
|
||||
email: string;
|
||||
expires_at: string;
|
||||
plan: string;
|
||||
storage_capacity: number,
|
||||
storage_capacity: number;
|
||||
organization: string;
|
||||
}
|
||||
|
||||
@@ -13,35 +13,35 @@
|
||||
//
|
||||
// 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/>.
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { Fragment, useState, useEffect } from "react";
|
||||
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
||||
import { AppState } from "../../../store";
|
||||
import { connect } from "react-redux";
|
||||
import { logMessageReceived, logResetMessages } from "./actions";
|
||||
import { LogMessage } from "./types";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { timeFromDate } from "../../../common/utils";
|
||||
import { wsProtocol } from "../../../utils/wsUtils";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { connect } from "react-redux";
|
||||
import { Grid } from "@material-ui/core";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import moment from "moment/moment";
|
||||
import { AppState } from "../../../../store";
|
||||
import { logMessageReceived, logResetMessages } from "../actions";
|
||||
import { LogMessage } from "../types";
|
||||
import { timeFromDate } from "../../../../common/utils";
|
||||
import { wsProtocol } from "../../../../utils/wsUtils";
|
||||
import {
|
||||
actionsTray,
|
||||
logsCommon,
|
||||
searchField,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
logList: {
|
||||
background: "#fff",
|
||||
minHeight: 400,
|
||||
height: "calc(100vh - 270px)",
|
||||
height: "calc(100vh - 304px)",
|
||||
overflow: "auto",
|
||||
fontSize: 13,
|
||||
padding: "25px 45px",
|
||||
padding: "25px 45px 0",
|
||||
border: "1px solid #EAEDEE",
|
||||
borderRadius: 4,
|
||||
},
|
||||
@@ -65,7 +65,7 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
...logsCommon,
|
||||
});
|
||||
|
||||
interface ILogs {
|
||||
@@ -73,11 +73,9 @@ interface ILogs {
|
||||
logMessageReceived: typeof logMessageReceived;
|
||||
logResetMessages: typeof logResetMessages;
|
||||
messages: LogMessage[];
|
||||
namespace: string;
|
||||
tenant: string;
|
||||
}
|
||||
|
||||
const Logs = ({
|
||||
const ErrorLogs = ({
|
||||
classes,
|
||||
logMessageReceived,
|
||||
logResetMessages,
|
||||
@@ -108,8 +106,9 @@ const Logs = ({
|
||||
};
|
||||
c.onmessage = (message: IMessageEvent) => {
|
||||
// console.log(message.data.toString())
|
||||
// FORMAT: 00:35:17 UTC 01/01/2021
|
||||
let m: LogMessage = JSON.parse(message.data.toString());
|
||||
m.time = new Date(m.time.toString());
|
||||
m.time = moment(m.time, "HH:mm:s UTC MM/DD/YYYY").toDate();
|
||||
m.key = Math.random();
|
||||
logMessageReceived(m);
|
||||
};
|
||||
@@ -323,38 +322,35 @@ const Logs = ({
|
||||
});
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<PageHeader label="Logs" />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Highlight Line"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setHighlight(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.logList}>{renderLines}</div>
|
||||
</Grid>
|
||||
<Fragment>
|
||||
<Grid container className={classes.logsSubContainer}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Highlight Line"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setHighlight(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.logList}>{renderLines}</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -367,4 +363,4 @@ const connector = connect(mapState, {
|
||||
logResetMessages: logResetMessages,
|
||||
});
|
||||
|
||||
export default connector(withStyles(styles)(Logs));
|
||||
export default withStyles(styles)(connector(ErrorLogs));
|
||||
427
portal-ui/src/screens/Console/Logs/LogSearch/LogsSearchMain.tsx
Normal file
427
portal-ui/src/screens/Console/Logs/LogSearch/LogsSearchMain.tsx
Normal file
@@ -0,0 +1,427 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
import React, { Fragment, useState, useEffect, useCallback } from "react";
|
||||
import get from "lodash/get";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Button, Grid } from "@material-ui/core";
|
||||
import { ArrowDropUp } from "@material-ui/icons";
|
||||
import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown";
|
||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import ErrorBlock from "../../../shared/ErrorBlock";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
logsCommon,
|
||||
searchField,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { IReqInfoSearchResults, ISearchResponse } from "./types";
|
||||
import { niceBytes, nsToSeconds } from "../../../../common/utils";
|
||||
import api from "../../../../common/api";
|
||||
import FilterInputWrapper from "../../Common/FormComponents/FilterInputWrapper/FilterInputWrapper";
|
||||
import DateTimePickerWrapper from "../../Common/FormComponents/DateTimePickerWrapper/DateTimePickerWrapper";
|
||||
|
||||
interface ILogSearchProps {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
inputBar: {
|
||||
flexGrow: 1,
|
||||
marginLeft: 15,
|
||||
},
|
||||
advancedLabel: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
color: "#091C42",
|
||||
border: 0,
|
||||
backgroundColor: "transparent",
|
||||
cursor: "pointer",
|
||||
"&:focus, &:active": {
|
||||
outline: "none",
|
||||
},
|
||||
},
|
||||
advancedLabelContainer: {
|
||||
marginTop: 10,
|
||||
},
|
||||
getInformationContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
orderButton: {
|
||||
width: 93,
|
||||
},
|
||||
recordsLabel: {
|
||||
alignSelf: "center",
|
||||
marginLeft: 15,
|
||||
},
|
||||
blockCollapsed: {
|
||||
height: 0,
|
||||
overflowY: "hidden",
|
||||
transitionDuration: "0.3s",
|
||||
},
|
||||
filterOpen: {
|
||||
height: 200,
|
||||
marginBottom: 12,
|
||||
},
|
||||
endLineAction: {
|
||||
marginBottom: 15,
|
||||
},
|
||||
filtersContainer: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: 12,
|
||||
},
|
||||
innerContainer: {
|
||||
backgroundColor: "#fff",
|
||||
border: "#EAEDEE 1px solid",
|
||||
borderRadius: 3,
|
||||
padding: 10,
|
||||
marginBottom: 15,
|
||||
},
|
||||
noticeLabel: {
|
||||
marginLeft: 15,
|
||||
marginBottom: 15,
|
||||
fontSize: 12,
|
||||
color: "#9C9C9C",
|
||||
},
|
||||
|
||||
tableFOpen: {
|
||||
height: "calc(100vh - 561px)",
|
||||
},
|
||||
tableFClosed: {
|
||||
height: "calc(100vh - 349px)",
|
||||
},
|
||||
"@global": {
|
||||
".overrideMargin": {
|
||||
marginLeft: 0,
|
||||
},
|
||||
},
|
||||
...searchField,
|
||||
...actionsTray,
|
||||
...logsCommon,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
const LogsSearchMain = ({ classes }: ILogSearchProps) => {
|
||||
const [error, setError] = useState<string>("");
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [timeStart, setTimeStart] = useState<any>(null);
|
||||
const [timeEnd, setTimeEnd] = useState<any>(null);
|
||||
const [filterOpen, setFilterOpen] = useState<boolean>(false);
|
||||
const [records, setRecords] = useState<IReqInfoSearchResults[]>([]);
|
||||
const [bucket, setBucket] = useState<string>("");
|
||||
const [apiName, setApiName] = useState<string>("");
|
||||
const [userAgent, setUserAgent] = useState<string>("");
|
||||
const [object, setObject] = useState<string>("");
|
||||
const [requestID, setRequestID] = useState<string>("");
|
||||
const [responseStatus, setResponseStatus] = useState<string>("");
|
||||
const [sortOrder, setSortOrder] = useState<"ASC" | "DESC" | undefined>(
|
||||
"DESC"
|
||||
);
|
||||
const [columnsShown, setColumnsShown] = useState<string[]>([
|
||||
"time",
|
||||
"api_name",
|
||||
"bucket",
|
||||
"object",
|
||||
"remote_host",
|
||||
"request_id",
|
||||
"user_agent",
|
||||
"response_status",
|
||||
]);
|
||||
const [nextPage, setNextPage] = useState<number>(0);
|
||||
const [alreadyFetching, setAlreadyFetching] = useState<boolean>(false);
|
||||
|
||||
let recordsResp: any = null;
|
||||
|
||||
const fetchRecords = useCallback(() => {
|
||||
if (!alreadyFetching) {
|
||||
setAlreadyFetching(true);
|
||||
let queryParams = `${bucket !== "" ? `&fp=bucket:${bucket}` : ""}${
|
||||
object !== "" ? `&fp=object:${object}` : ""
|
||||
}${apiName !== "" ? `&fp=api_name:${apiName}` : ""}${
|
||||
requestID !== "" ? `&fp=request_id:${requestID}` : ""
|
||||
}${userAgent !== "" ? `&fp=user_agent:${userAgent}` : ""}${
|
||||
responseStatus !== "" ? `&fp=response_status:${responseStatus}` : ""
|
||||
}`;
|
||||
|
||||
queryParams = queryParams.trim();
|
||||
|
||||
if (queryParams.endsWith(",")) {
|
||||
queryParams = queryParams.slice(0, -1);
|
||||
}
|
||||
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/logs/search?q=reqinfo${
|
||||
queryParams !== "" ? `${queryParams}` : ""
|
||||
}&pageSize=100&pageNo=${nextPage}&order=${
|
||||
sortOrder === "DESC" ? "timeDesc" : "timeAsc"
|
||||
}${
|
||||
timeStart !== null ? `&timeStart=${timeStart.toISOString()}` : ""
|
||||
}${timeEnd !== null ? `&timeEnd=${timeEnd.toISOString()}` : ""}`
|
||||
)
|
||||
.then((res: ISearchResponse) => {
|
||||
const fetchedResults = res.results || [];
|
||||
const newResultSet = [...records, ...fetchedResults];
|
||||
|
||||
setLoading(false);
|
||||
setAlreadyFetching(false);
|
||||
setRecords(newResultSet);
|
||||
setError("");
|
||||
setNextPage(nextPage + 1);
|
||||
|
||||
if (recordsResp !== null) {
|
||||
recordsResp();
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
setLoading(false);
|
||||
setAlreadyFetching(false);
|
||||
setError(err);
|
||||
});
|
||||
}
|
||||
}, [
|
||||
bucket,
|
||||
object,
|
||||
apiName,
|
||||
requestID,
|
||||
userAgent,
|
||||
responseStatus,
|
||||
nextPage,
|
||||
sortOrder,
|
||||
timeStart,
|
||||
timeEnd,
|
||||
alreadyFetching,
|
||||
records,
|
||||
recordsResp,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
setRecords([]);
|
||||
fetchRecords();
|
||||
}
|
||||
}, [loading, sortOrder, fetchRecords]);
|
||||
|
||||
const triggerLoad = () => {
|
||||
setNextPage(0);
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
const selectColumn = (colName: string, active: boolean) => {
|
||||
let newArray = [...columnsShown];
|
||||
|
||||
if (!active) {
|
||||
newArray = columnsShown.filter((element) => element !== colName);
|
||||
} else {
|
||||
if (!newArray.includes(colName)) {
|
||||
newArray.push(colName);
|
||||
}
|
||||
}
|
||||
setColumnsShown(newArray);
|
||||
};
|
||||
|
||||
const sortChange = (sortData: any) => {
|
||||
const newSortDirection = get(sortData, "sortDirection", "DESC");
|
||||
setSortOrder(newSortDirection);
|
||||
setNextPage(0);
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
const loadMoreRecords = (_: { startIndex: number; stopIndex: number }) => {
|
||||
fetchRecords();
|
||||
return new Promise((resolve) => {
|
||||
recordsResp = resolve;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Grid container className={classes.logsSubContainer}>
|
||||
{error !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<ErrorBlock errorMessage={error} />
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={`${classes.actionsTray} ${classes.timeContainers}`}
|
||||
>
|
||||
<span className={classes.label}>Start Time</span>
|
||||
<DateTimePickerWrapper value={timeStart} onChange={setTimeStart} />
|
||||
<span className={classes.label}>End Time</span>
|
||||
<DateTimePickerWrapper value={timeEnd} onChange={setTimeEnd} />
|
||||
</Grid>
|
||||
<Grid item xs={12} className={`${classes.advancedLabelContainer}`}>
|
||||
<div
|
||||
className={`${classes.blockCollapsed} ${
|
||||
filterOpen ? classes.filterOpen : ""
|
||||
}`}
|
||||
>
|
||||
<div className={classes.innerContainer}>
|
||||
<div className={classes.noticeLabel}>
|
||||
Enable your preferred options to get filtered records.
|
||||
<br />
|
||||
You can use '*' to match any character, '.' to signify a single
|
||||
character or '\' to scape an special character (E.g. mybucket-*)
|
||||
</div>
|
||||
<div className={classes.filtersContainer}>
|
||||
<FilterInputWrapper
|
||||
onChange={setBucket}
|
||||
value={bucket}
|
||||
label={"Bucket"}
|
||||
id="bucket"
|
||||
name="bucket"
|
||||
/>
|
||||
<FilterInputWrapper
|
||||
onChange={setApiName}
|
||||
value={apiName}
|
||||
label={"API Name"}
|
||||
id="api_name"
|
||||
name="api_name"
|
||||
/>
|
||||
<FilterInputWrapper
|
||||
onChange={setUserAgent}
|
||||
value={userAgent}
|
||||
label={"User Agent"}
|
||||
id="user_agent"
|
||||
name="user_agent"
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.filtersContainer}>
|
||||
<FilterInputWrapper
|
||||
onChange={setObject}
|
||||
value={object}
|
||||
label={"Object"}
|
||||
id="object"
|
||||
name="object"
|
||||
/>
|
||||
<FilterInputWrapper
|
||||
onChange={setRequestID}
|
||||
value={requestID}
|
||||
label={"Request ID"}
|
||||
id="request_id"
|
||||
name="request_id"
|
||||
/>
|
||||
<FilterInputWrapper
|
||||
onChange={setResponseStatus}
|
||||
value={responseStatus}
|
||||
label={"Response Status"}
|
||||
id="response_status"
|
||||
name="response_status"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={`${classes.actionsTray} ${classes.endLineAction}`}
|
||||
>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className={`${classes.advancedLabel} overrideMargin`}
|
||||
onClick={() => {
|
||||
setFilterOpen(!filterOpen);
|
||||
}}
|
||||
>
|
||||
Advanced Filters{" "}
|
||||
{filterOpen ? <ArrowDropUp /> : <ArrowDropDownIcon />}
|
||||
</button>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={triggerLoad}
|
||||
>
|
||||
Get Information
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
columns={[
|
||||
{ label: "Timestamp", elementKey: "time", enableSort: true },
|
||||
{ label: "API Name", elementKey: "api_name" },
|
||||
{ label: "Bucket", elementKey: "bucket" },
|
||||
{ label: "Object", elementKey: "object" },
|
||||
{ label: "Remote Host", elementKey: "remote_host" },
|
||||
{ label: "Request ID", elementKey: "request_id" },
|
||||
{ label: "User Agent", elementKey: "user_agent" },
|
||||
{
|
||||
label: "Response Status",
|
||||
elementKey: "response_status",
|
||||
renderFunction: (element) => (
|
||||
<Fragment>
|
||||
<span>
|
||||
{element.response_status_code} ({element.response_status})
|
||||
</span>
|
||||
</Fragment>
|
||||
),
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Request Content Length",
|
||||
elementKey: "request_content_length",
|
||||
renderFunction: niceBytes,
|
||||
},
|
||||
{
|
||||
label: "Response Content Length",
|
||||
elementKey: "response_content_length",
|
||||
renderFunction: niceBytes,
|
||||
},
|
||||
{
|
||||
label: "Time to Response NS",
|
||||
elementKey: "time_to_response_ns",
|
||||
renderFunction: nsToSeconds,
|
||||
contentTextAlign: "right",
|
||||
},
|
||||
]}
|
||||
isLoading={loading}
|
||||
records={records}
|
||||
entityName="Logs"
|
||||
customEmptyMessage={"There is no information with this criteria"}
|
||||
idField="request_id"
|
||||
columnsSelector
|
||||
columnsShown={columnsShown}
|
||||
onColumnChange={selectColumn}
|
||||
customPaperHeight={
|
||||
filterOpen ? classes.tableFOpen : classes.tableFClosed
|
||||
}
|
||||
sortConfig={{
|
||||
currentSort: "time",
|
||||
currentDirection: sortOrder,
|
||||
triggerSort: sortChange,
|
||||
}}
|
||||
infiniteScrollConfig={{
|
||||
recordsCount: 1000000,
|
||||
loadMoreRecords: loadMoreRecords,
|
||||
}}
|
||||
textSelectable
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(LogsSearchMain);
|
||||
34
portal-ui/src/screens/Console/Logs/LogSearch/types.ts
Normal file
34
portal-ui/src/screens/Console/Logs/LogSearch/types.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
export interface IReqInfoSearchResults {
|
||||
id: string;
|
||||
api_name: string;
|
||||
bucket: string;
|
||||
object: string;
|
||||
time_to_response_ns: number;
|
||||
remote_host: string;
|
||||
request_id: string;
|
||||
user_agent: string;
|
||||
response_status: string;
|
||||
response_status_code: number;
|
||||
request_content_length: any;
|
||||
response_content_length: any;
|
||||
}
|
||||
|
||||
export interface ISearchResponse {
|
||||
results: IReqInfoSearchResults[];
|
||||
}
|
||||
66
portal-ui/src/screens/Console/Logs/LogsMain.tsx
Normal file
66
portal-ui/src/screens/Console/Logs/LogsMain.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import React, { Fragment, useState } from "react";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import { Grid } from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Tab from "@material-ui/core/Tab";
|
||||
import Tabs from "@material-ui/core/Tabs";
|
||||
import ErrorLogs from "./ErrorLogs/ErrorLogs";
|
||||
import LogsSearchMain from "./LogSearch/LogsSearchMain";
|
||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||
|
||||
interface ILogsMainProps {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
headerLabel: {
|
||||
fontSize: 22,
|
||||
fontWeight: 600,
|
||||
color: "#000",
|
||||
marginTop: 4,
|
||||
},
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
const LogsMain = ({ classes }: ILogsMainProps) => {
|
||||
const [currentTab, setCurrentTab] = useState<number>(0);
|
||||
return (
|
||||
<Fragment>
|
||||
<PageHeader label="Logs" />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.headerLabel}>
|
||||
All Logs
|
||||
</Grid>
|
||||
<Tabs
|
||||
value={currentTab}
|
||||
onChange={(e: React.ChangeEvent<{}>, newValue: number) => {
|
||||
setCurrentTab(newValue);
|
||||
}}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
aria-label="cluster-tabs"
|
||||
>
|
||||
<Tab label="Error Logs" />
|
||||
<Tab label="Logs Search" />
|
||||
</Tabs>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{currentTab === 0 && (
|
||||
<Grid item xs={12}>
|
||||
<ErrorLogs />
|
||||
</Grid>
|
||||
)}
|
||||
{currentTab === 1 && (
|
||||
<Grid item xs={12}>
|
||||
<LogsSearchMain />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(LogsMain);
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
getBytes,
|
||||
k8sfactorForDropdown,
|
||||
niceBytes,
|
||||
setMemoryResource,
|
||||
} from "../../../../common/utils";
|
||||
import {
|
||||
commonFormValidation,
|
||||
@@ -54,7 +55,14 @@ import {
|
||||
IErasureCodeCalc,
|
||||
ITenantCreator,
|
||||
} from "../../../../common/types";
|
||||
import { ecListTransform, Opts } from "./utils";
|
||||
import {
|
||||
ecListTransform,
|
||||
getLimitSizes,
|
||||
IQuotaElement,
|
||||
IQuotas,
|
||||
Opts,
|
||||
} from "./utils";
|
||||
import { IMemorySize } from "./types";
|
||||
|
||||
interface IAddTenantProps {
|
||||
open: boolean;
|
||||
@@ -168,6 +176,13 @@ const AddTenant = ({
|
||||
const [drivesPerServer, setDrivesPerServer] = useState<string>("1");
|
||||
const [memoryNode, setMemoryNode] = useState<string>("2");
|
||||
const [ecParity, setECParity] = useState<string>("");
|
||||
const [maxAllocableMemo, setMaxAllocableMemo] = useState<number>(0);
|
||||
const [limitSize, setLimitSize] = useState<any>({});
|
||||
const [memorySize, setMemorySize] = useState<IMemorySize>({
|
||||
error: "",
|
||||
limit: 0,
|
||||
request: 0,
|
||||
});
|
||||
const [distribution, setDistribution] = useState<any>({
|
||||
error: "",
|
||||
nodes: 0,
|
||||
@@ -183,6 +198,8 @@ const AddTenant = ({
|
||||
rawCapacity: "0",
|
||||
storageFactors: [],
|
||||
});
|
||||
const [exposeMinIO, setExposeMinIO] = useState<boolean>(true);
|
||||
const [exposeConsole, setExposeConsole] = useState<boolean>(true);
|
||||
|
||||
// Forms Validation
|
||||
const [nameTenantValid, setNameTenantValid] = useState<boolean>(false);
|
||||
@@ -244,8 +261,9 @@ const AddTenant = ({
|
||||
"GET",
|
||||
`/api/v1/namespaces/${namespace}/resourcequotas/${namespace}-storagequota`
|
||||
)
|
||||
.then((res: string[]) => {
|
||||
const elements = get(res, "elements", []);
|
||||
.then((res: IQuotas) => {
|
||||
const elements: IQuotaElement[] = get(res, "elements", []);
|
||||
setLimitSize(getLimitSizes(res));
|
||||
|
||||
const newStorage = elements.map((storageClass: any) => {
|
||||
const name = get(storageClass, "name", "").split(
|
||||
@@ -266,6 +284,47 @@ const AddTenant = ({
|
||||
});
|
||||
};
|
||||
|
||||
const validateMemorySize = useCallback(() => {
|
||||
const memSize = parseInt(memoryNode) || 0;
|
||||
const clusterSize = volumeSize || 0;
|
||||
const maxMemSize = maxAllocableMemo || 0;
|
||||
const clusterSizeFactor = sizeFactor;
|
||||
|
||||
const clusterSizeBytes = getBytes(
|
||||
clusterSize.toString(10),
|
||||
clusterSizeFactor
|
||||
);
|
||||
const memoSize = setMemoryResource(memSize, clusterSizeBytes, maxMemSize);
|
||||
|
||||
setMemorySize(memoSize);
|
||||
}, [maxAllocableMemo, memoryNode, sizeFactor, volumeSize]);
|
||||
|
||||
const getMaxAllocableMemory = (nodes: string) => {
|
||||
if (nodes !== "" && !isNaN(parseInt(nodes))) {
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/cluster/max-allocatable-memory?num_nodes=${nodes}`
|
||||
)
|
||||
.then((res: { max_memory: number }) => {
|
||||
const maxMemory = res.max_memory ? res.max_memory : 0;
|
||||
setMaxAllocableMemo(maxMemory);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
setMaxAllocableMemo(0);
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
validateMemorySize();
|
||||
}, [memoryNode, validateMemorySize]);
|
||||
|
||||
useEffect(() => {
|
||||
validateMemorySize();
|
||||
}, [maxAllocableMemo, validateMemorySize]);
|
||||
|
||||
const debounceNamespace = useCallback(
|
||||
debounce(getNamespaceInformation, 500),
|
||||
[namespace]
|
||||
@@ -299,6 +358,7 @@ const AddTenant = ({
|
||||
useEffect(() => {
|
||||
validateClusterSize();
|
||||
getECValue();
|
||||
getMaxAllocableMemory(nodes);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [nodes, volumeSize, sizeFactor, drivesPerServer]);
|
||||
|
||||
@@ -355,7 +415,7 @@ const AddTenant = ({
|
||||
{
|
||||
fieldKey: "namespace",
|
||||
required: true,
|
||||
value: tenantName,
|
||||
value: namespace,
|
||||
customValidation: storageClasses.length < 1,
|
||||
customValidationMessage: "Please enter a valid namespace",
|
||||
},
|
||||
@@ -385,8 +445,13 @@ const AddTenant = ({
|
||||
fieldKey: "volume_size",
|
||||
required: true,
|
||||
value: volumeSize,
|
||||
customValidation: parseInt(parsedSize) < 1073741824,
|
||||
customValidationMessage: "Volume size must be greater than 1Gi",
|
||||
customValidation:
|
||||
parseInt(parsedSize) < 1073741824 ||
|
||||
parseInt(parsedSize) > limitSize[selectedStorageClass],
|
||||
customValidationMessage: `Volume size must be greater than 1Gi and less than ${niceBytes(
|
||||
limitSize[selectedStorageClass],
|
||||
true
|
||||
)}`,
|
||||
},
|
||||
{
|
||||
fieldKey: "memory_per_node",
|
||||
@@ -410,7 +475,8 @@ const AddTenant = ({
|
||||
!("memory_per_node" in commonValidation) &&
|
||||
!("drivesps" in commonValidation) &&
|
||||
distribution.error === "" &&
|
||||
ecParityCalc.error === 0
|
||||
ecParityCalc.error === 0 &&
|
||||
memorySize.error === ""
|
||||
);
|
||||
|
||||
setValidationErrors(commonValidation);
|
||||
@@ -422,6 +488,9 @@ const AddTenant = ({
|
||||
distribution,
|
||||
drivesPerServer,
|
||||
ecParityCalc,
|
||||
memorySize,
|
||||
limitSize,
|
||||
selectedStorageClass,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -830,6 +899,8 @@ const AddTenant = ({
|
||||
service_name: "",
|
||||
image: imageName,
|
||||
console_image: consoleImage,
|
||||
expose_minio: exposeMinIO,
|
||||
expose_console: exposeConsole,
|
||||
pools: [
|
||||
{
|
||||
name: poolName,
|
||||
@@ -841,7 +912,10 @@ const AddTenant = ({
|
||||
},
|
||||
resources: {
|
||||
requests: {
|
||||
memory: parseInt(getBytes(memoryNode, "GiB")),
|
||||
memory: memorySize.request,
|
||||
},
|
||||
limits: {
|
||||
memory: memorySize.limit,
|
||||
},
|
||||
},
|
||||
affinity: hardCodedAffinity,
|
||||
@@ -1260,6 +1334,42 @@ const AddTenant = ({
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
<div className={classes.headerElement}>
|
||||
<h3 className={classes.h3Section}>Expose Services</h3>
|
||||
<span className={classes.descriptionText}>
|
||||
Whether the tenant's services should request an external IP.
|
||||
</span>
|
||||
</div>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="expose_minio"
|
||||
id="expose_minio"
|
||||
name="expose_minio"
|
||||
checked={exposeMinIO}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
setExposeMinIO(checked);
|
||||
}}
|
||||
label={"Expose MiniO Service"}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="expose_console"
|
||||
id="expose_console"
|
||||
name="expose_console"
|
||||
checked={exposeConsole}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
setExposeConsole(checked);
|
||||
}}
|
||||
label={"Expose Console Service"}
|
||||
/>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
),
|
||||
buttons: [
|
||||
@@ -2082,6 +2192,7 @@ const AddTenant = ({
|
||||
</span>
|
||||
</div>
|
||||
<span className={classes.error}>{distribution.error}</span>
|
||||
<span className={classes.error}>{memorySize.error}</span>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="nodes"
|
||||
@@ -2125,7 +2236,7 @@ const AddTenant = ({
|
||||
setVolumeSize(e.target.value);
|
||||
clearValidationError("volume_size");
|
||||
}}
|
||||
label="Size"
|
||||
label="Total Size"
|
||||
value={volumeSize}
|
||||
required
|
||||
error={validationErrors["volume_size"] || ""}
|
||||
@@ -2146,39 +2257,41 @@ const AddTenant = ({
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
id="memory_per_node"
|
||||
name="memory_per_node"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setMemoryNode(e.target.value);
|
||||
clearValidationError("memory_per_node");
|
||||
}}
|
||||
label="Memory per Node [Gi]"
|
||||
value={memoryNode}
|
||||
required
|
||||
error={validationErrors["memory_per_node"] || ""}
|
||||
min="2"
|
||||
/>
|
||||
</Grid>
|
||||
{advancedMode && (
|
||||
<Grid item xs={12}>
|
||||
<SelectWrapper
|
||||
id="ec_parity"
|
||||
name="ec_parity"
|
||||
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||
setECParity(e.target.value as string);
|
||||
}}
|
||||
label="Erasure Code Parity"
|
||||
value={ecParity}
|
||||
options={ecParityChoices}
|
||||
/>
|
||||
<span className={classes.descriptionText}>
|
||||
Please select the desired parity. This setting will change the
|
||||
max usable capacity in the cluster
|
||||
</span>
|
||||
</Grid>
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
id="memory_per_node"
|
||||
name="memory_per_node"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setMemoryNode(e.target.value);
|
||||
clearValidationError("memory_per_node");
|
||||
}}
|
||||
label="Memory per Node [Gi]"
|
||||
value={memoryNode}
|
||||
required
|
||||
error={validationErrors["memory_per_node"] || ""}
|
||||
min="2"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<SelectWrapper
|
||||
id="ec_parity"
|
||||
name="ec_parity"
|
||||
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||
setECParity(e.target.value as string);
|
||||
}}
|
||||
label="Erasure Code Parity"
|
||||
value={ecParity}
|
||||
options={ecParityChoices}
|
||||
/>
|
||||
<span className={classes.descriptionText}>
|
||||
Please select the desired parity. This setting will change the
|
||||
max usable capacity in the cluster
|
||||
</span>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
<h4>Resource Allocation</h4>
|
||||
<Table className={classes.table} aria-label="simple table">
|
||||
@@ -2215,6 +2328,14 @@ const AddTenant = ({
|
||||
{distribution ? distribution.persistentVolumes : "-"}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{!advancedMode && (
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">
|
||||
Memory per Node
|
||||
</TableCell>
|
||||
<TableCell align="right">{memoryNode} Gi</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{ecParityCalc.error === 0 && usableInformation && (
|
||||
@@ -2323,7 +2444,7 @@ const AddTenant = ({
|
||||
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Volume Size
|
||||
Total Size
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{volumeSize} {sizeFactor}
|
||||
|
||||
@@ -39,6 +39,11 @@ export interface IVolumeConfiguration {
|
||||
labels: { [key: string]: any } | null;
|
||||
}
|
||||
|
||||
export interface IEndpoints {
|
||||
minio: string;
|
||||
console: string;
|
||||
}
|
||||
|
||||
export interface ITenant {
|
||||
total_size: number;
|
||||
name: string;
|
||||
@@ -53,6 +58,7 @@ export interface ITenant {
|
||||
volume_count: number;
|
||||
volumes_per_server: number;
|
||||
pools: IPool[];
|
||||
endpoints: IEndpoints;
|
||||
// computed
|
||||
capacity: string;
|
||||
subnet_license: LicenseInfo;
|
||||
@@ -61,3 +67,9 @@ export interface ITenant {
|
||||
export interface ITenantsResponse {
|
||||
tenants: ITenant[];
|
||||
}
|
||||
|
||||
export interface IMemorySize {
|
||||
error: string;
|
||||
limit: number;
|
||||
request: number;
|
||||
}
|
||||
|
||||
@@ -14,13 +14,45 @@
|
||||
// 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/>.
|
||||
|
||||
import get from "lodash/get";
|
||||
|
||||
export interface Opts {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface IQuotaElement {
|
||||
hard: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface IQuotas {
|
||||
elements?: IQuotaElement[];
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const minMemReq = 2147483648;
|
||||
|
||||
export const ecListTransform = (ecList: string[]): Opts[] => {
|
||||
return ecList.map((value) => {
|
||||
return { label: value, value };
|
||||
});
|
||||
};
|
||||
|
||||
export const getLimitSizes = (resourceQuotas: IQuotas) => {
|
||||
const quotas: IQuotaElement[] = get(resourceQuotas, "elements", []);
|
||||
|
||||
const returnQuotas: any = {};
|
||||
|
||||
quotas.forEach((rsQuota) => {
|
||||
const stCName = rsQuota.name.split(
|
||||
".storageclass.storage.k8s.io/requests.storage"
|
||||
)[0];
|
||||
const hard = get(rsQuota, "hard", 0);
|
||||
const used = get(rsQuota, "used", 0);
|
||||
|
||||
returnQuotas[stCName] = hard - used;
|
||||
});
|
||||
|
||||
return returnQuotas;
|
||||
};
|
||||
|
||||
@@ -329,6 +329,30 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
||||
<div>{instances}</div>
|
||||
<div>Volumes:</div>
|
||||
<div>{volumes}</div>
|
||||
{tenant?.endpoints && (
|
||||
<React.Fragment>
|
||||
<div>Endpoint:</div>
|
||||
<div>
|
||||
<a
|
||||
href={tenant?.endpoints.minio}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{tenant?.endpoints.minio}
|
||||
</a>
|
||||
</div>
|
||||
<div>Console:</div>
|
||||
<div>
|
||||
<a
|
||||
href={tenant?.endpoints.console}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{tenant?.endpoints.console}
|
||||
</a>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
</Paper>
|
||||
</Grid>
|
||||
|
||||
@@ -27,6 +27,7 @@ import { containerForHeader } from "../Common/FormComponents/common/styleLibrary
|
||||
import { Grid } from "@material-ui/core";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import moment from "moment/moment";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -89,7 +90,7 @@ const Trace = ({
|
||||
};
|
||||
c.onmessage = (message: IMessageEvent) => {
|
||||
let m: TraceMessage = JSON.parse(message.data.toString());
|
||||
m.time = new Date(m.time.toString());
|
||||
m.ptime = moment(m.time, "YYYY-MM-DD HH:mm:s.SSSS +0000 UTC").toDate();
|
||||
m.key = Math.random();
|
||||
traceMessageReceived(m);
|
||||
};
|
||||
@@ -115,7 +116,7 @@ const Trace = ({
|
||||
columns={[
|
||||
{
|
||||
label: "Time",
|
||||
elementKey: "time",
|
||||
elementKey: "ptime",
|
||||
renderFunction: (time: Date) => {
|
||||
const timeParse = new Date(time);
|
||||
return timeFromDate(timeParse);
|
||||
@@ -160,6 +161,7 @@ const Trace = ({
|
||||
entityName="Traces"
|
||||
idField="api"
|
||||
customEmptyMessage="There are no traced Elements yet"
|
||||
autoScrollToBottom
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -23,7 +23,8 @@ export interface CallStats {
|
||||
|
||||
export interface TraceMessage {
|
||||
client: string;
|
||||
time: Date;
|
||||
time: string;
|
||||
ptime: Date;
|
||||
statusCode: number;
|
||||
api: string;
|
||||
query: string;
|
||||
|
||||
23
portal-ui/src/screens/shared/__tests__/ErrorBlock.test.tsx
Normal file
23
portal-ui/src/screens/shared/__tests__/ErrorBlock.test.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import ErrorBlock from "../ErrorBlock";
|
||||
|
||||
it("renders without crashing", () => {
|
||||
const div = document.createElement("div");
|
||||
ReactDOM.render(<ErrorBlock errorMessage={""} />, div);
|
||||
});
|
||||
@@ -1090,7 +1090,7 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
|
||||
version "7.12.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
|
||||
integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
|
||||
@@ -1148,6 +1148,18 @@
|
||||
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18"
|
||||
integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==
|
||||
|
||||
"@date-io/core@1.x", "@date-io/core@^1.3.13":
|
||||
version "1.3.13"
|
||||
resolved "https://registry.yarnpkg.com/@date-io/core/-/core-1.3.13.tgz#90c71da493f20204b7a972929cc5c482d078b3fa"
|
||||
integrity sha512-AlEKV7TxjeK+jxWVKcCFrfYAk8spX9aCyiToFIiLPtfQbsjmRGLIhb5VZgptQcJdHtLXo7+m0DuurwFgUToQuA==
|
||||
|
||||
"@date-io/moment@1.x":
|
||||
version "1.3.13"
|
||||
resolved "https://registry.yarnpkg.com/@date-io/moment/-/moment-1.3.13.tgz#56c2772bc4f6675fc6970257e6033e7a7c2960f0"
|
||||
integrity sha512-3kJYusJtQuOIxq6byZlzAHoW/18iExJer9qfRF5DyyzdAk074seTuJfdofjz4RFfTd/Idk8WylOQpWtERqvFuQ==
|
||||
dependencies:
|
||||
"@date-io/core" "^1.3.13"
|
||||
|
||||
"@emotion/hash@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
|
||||
@@ -1367,6 +1379,18 @@
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.4.4"
|
||||
|
||||
"@material-ui/pickers@^3.2.10":
|
||||
version "3.2.10"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/pickers/-/pickers-3.2.10.tgz#19df024895876eb0ec7cd239bbaea595f703f0ae"
|
||||
integrity sha512-B8G6Obn5S3RCl7hwahkQj9sKUapwXWFjiaz/Bsw1fhYFdNMnDUolRiWQSoKPb1/oKe37Dtfszoywi1Ynbo3y8w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.0"
|
||||
"@date-io/core" "1.x"
|
||||
"@types/styled-jsx" "^2.2.8"
|
||||
clsx "^1.0.2"
|
||||
react-transition-group "^4.0.0"
|
||||
rifm "^0.7.0"
|
||||
|
||||
"@material-ui/styles@^4.11.2":
|
||||
version "4.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.2.tgz#e70558be3f41719e8c0d63c7a3c9ae163fdc84cb"
|
||||
@@ -1686,6 +1710,13 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-grid-layout@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-grid-layout/-/react-grid-layout-1.1.1.tgz#1eb322fa88cd1475bdc60ecca9676f6853e18887"
|
||||
integrity sha512-bvPkITzwGGOZKjp01nVSgPrdfGm/uTa5t8Odd8vQRXJsLj7uZLZXSXgWr+TiXBAkUsmHPxhsyswXQCiFeDuZnQ==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-redux@^7.1.5":
|
||||
version "7.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.12.tgz#148f2c768687346b556e29a322ca44cfa28cc3ac"
|
||||
@@ -1749,6 +1780,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
|
||||
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
|
||||
|
||||
"@types/styled-jsx@^2.2.8":
|
||||
version "2.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/styled-jsx/-/styled-jsx-2.2.8.tgz#b50d13d8a3c34036282d65194554cf186bab7234"
|
||||
integrity sha512-Yjye9VwMdYeXfS71ihueWRSxrruuXTwKCbzue4+5b2rjnQ//AtyM7myZ1BEhNhBQ/nL/RE7bdToUoLln2miKvg==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/superagent@^4.1.4":
|
||||
version "4.1.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.10.tgz#5e2cc721edf58f64fe9b819f326ee74803adee86"
|
||||
@@ -3074,7 +3112,7 @@ class-utils@^0.3.5:
|
||||
isobject "^3.0.0"
|
||||
static-extend "^0.1.1"
|
||||
|
||||
classnames@^2.2.5:
|
||||
classnames@2.x, classnames@^2.2.5:
|
||||
version "2.2.6"
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
|
||||
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
|
||||
@@ -3137,7 +3175,7 @@ clone-deep@^4.0.1:
|
||||
kind-of "^6.0.2"
|
||||
shallow-clone "^3.0.0"
|
||||
|
||||
clsx@^1.0.4:
|
||||
clsx@^1.0.2, clsx@^1.0.4:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
||||
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
||||
@@ -7015,6 +7053,11 @@ lodash.debounce@^4.0.8:
|
||||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
|
||||
|
||||
lodash.isequal@^4.0.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
|
||||
|
||||
lodash.memoize@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
@@ -7379,7 +7422,7 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
moment@^2.10.2, moment@^2.24.0:
|
||||
moment@^2.10.2, moment@^2.29.1:
|
||||
version "2.29.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
||||
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
||||
@@ -8921,7 +8964,7 @@ prompts@^2.0.1:
|
||||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.5"
|
||||
|
||||
prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
prop-types@15.x, prop-types@^15.0.0, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
@@ -9169,11 +9212,30 @@ react-dom@17.0.1:
|
||||
object-assign "^4.1.1"
|
||||
scheduler "^0.20.1"
|
||||
|
||||
react-draggable@^4.0.0, react-draggable@^4.0.3:
|
||||
version "4.4.3"
|
||||
resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.3.tgz#0727f2cae5813e36b0e4962bf11b2f9ef2b406f3"
|
||||
integrity sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w==
|
||||
dependencies:
|
||||
classnames "^2.2.5"
|
||||
prop-types "^15.6.0"
|
||||
|
||||
react-error-overlay@^6.0.7:
|
||||
version "6.0.8"
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.8.tgz#474ed11d04fc6bda3af643447d85e9127ed6b5de"
|
||||
integrity sha512-HvPuUQnLp5H7TouGq3kzBeioJmXms1wHy9EGjz2OURWBp4qZO6AfGEcnxts1D/CbwPLRAgTMPCEgYhA3sEM4vw==
|
||||
|
||||
react-grid-layout@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-grid-layout/-/react-grid-layout-1.2.0.tgz#87124d549c86c8df8841666618c8c3e3cb205c26"
|
||||
integrity sha512-fJMGQFguphkAs0NsLNf8hz9cUv9B642JYei2yddiPby/X/kJ4HFIaMUhhqg1ArVfn/vHet1+h+LE4n85cFPh+Q==
|
||||
dependencies:
|
||||
classnames "2.x"
|
||||
lodash.isequal "^4.0.0"
|
||||
prop-types "^15.0.0"
|
||||
react-draggable "^4.0.0"
|
||||
react-resizable "^1.10.0"
|
||||
|
||||
react-hot-loader@^4.13.0:
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.13.0.tgz#c27e9408581c2a678f5316e69c061b226dc6a202"
|
||||
@@ -9219,6 +9281,14 @@ react-redux@^7.1.3:
|
||||
prop-types "^15.7.2"
|
||||
react-is "^16.13.1"
|
||||
|
||||
react-resizable@^1.10.0:
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/react-resizable/-/react-resizable-1.11.0.tgz#0b237c4aff16937b7663de1045861749683227ad"
|
||||
integrity sha512-VoGz2ddxUFvildS8r8/29UZJeyiM3QJnlmRZSuXm+FpTqq/eIrMPc796Y9XQLg291n2hFZJtIoP1xC3hSTw/jg==
|
||||
dependencies:
|
||||
prop-types "15.x"
|
||||
react-draggable "^4.0.3"
|
||||
|
||||
react-resize-detector@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-2.3.0.tgz#57bad1ae26a28a62a2ddb678ba6ffdf8fa2b599c"
|
||||
@@ -9338,7 +9408,7 @@ react-transition-group@^2.5.0:
|
||||
prop-types "^15.6.2"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
|
||||
react-transition-group@^4.4.0:
|
||||
react-transition-group@^4.0.0, react-transition-group@^4.4.0:
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
|
||||
integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==
|
||||
@@ -9793,6 +9863,13 @@ rgba-regex@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
|
||||
integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
|
||||
|
||||
rifm@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/rifm/-/rifm-0.7.0.tgz#debe951a9c83549ca6b33e5919f716044c2230be"
|
||||
integrity sha512-DSOJTWHD67860I5ojetXdEQRIBvF6YcpNe53j0vn1vp9EUb9N80EiZTxgP+FkDKorWC8PZw052kTF4C1GOivCQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
|
||||
rimraf@2.6.3:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
||||
|
||||
@@ -18,6 +18,14 @@ package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
@@ -29,7 +37,7 @@ import (
|
||||
func registerAdminInfoHandlers(api *operations.ConsoleAPI) {
|
||||
// return usage stats
|
||||
api.AdminAPIAdminInfoHandler = admin_api.AdminInfoHandlerFunc(func(params admin_api.AdminInfoParams, session *models.Principal) middleware.Responder {
|
||||
infoResp, err := getAdminInfoResponse(session)
|
||||
infoResp, err := getAdminInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewAdminInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -69,27 +77,605 @@ func getAdminInfo(ctx context.Context, client MinioAdmin) (*usageInfo, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Target struct {
|
||||
Expr string
|
||||
Interval string
|
||||
LegendFormat string
|
||||
}
|
||||
|
||||
type ReduceOptions struct {
|
||||
Calcs []string
|
||||
}
|
||||
|
||||
type MetricOptions struct {
|
||||
ReduceOptions ReduceOptions
|
||||
}
|
||||
|
||||
type Metric struct {
|
||||
Title string
|
||||
Type string
|
||||
Options MetricOptions
|
||||
Targets []Target
|
||||
}
|
||||
|
||||
type WidgetLabel struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
var labels = []WidgetLabel{
|
||||
{Name: "instance"},
|
||||
{Name: "disk"},
|
||||
}
|
||||
|
||||
var widgets = []Metric{
|
||||
{
|
||||
Title: "Uptime",
|
||||
Type: "stat",
|
||||
|
||||
Options: MetricOptions{
|
||||
ReduceOptions: ReduceOptions{
|
||||
Calcs: []string{
|
||||
"mean",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "time() - max(process_start_time_seconds)",
|
||||
LegendFormat: "{{instance}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Total Online disks",
|
||||
Type: "stat",
|
||||
|
||||
Options: MetricOptions{
|
||||
ReduceOptions: ReduceOptions{
|
||||
Calcs: []string{
|
||||
"mean",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "sum(minio_disks_total)",
|
||||
LegendFormat: "Total online disks in MinIO Cluster",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Total Data",
|
||||
Type: "gauge",
|
||||
|
||||
Options: MetricOptions{
|
||||
ReduceOptions: ReduceOptions{
|
||||
Calcs: []string{
|
||||
"lastNotNull",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "topk(1, sum(bucket_usage_size) by (instance))",
|
||||
LegendFormat: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Data Growth",
|
||||
Type: "graph",
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "topk(1, sum(bucket_usage_size) by (instance))",
|
||||
LegendFormat: "Total Storage Used",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Object size distribution",
|
||||
Type: "bargauge",
|
||||
|
||||
Options: MetricOptions{
|
||||
ReduceOptions: ReduceOptions{
|
||||
Calcs: []string{
|
||||
"mean",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "max by (object_size) (bucket_objects_histogram)",
|
||||
LegendFormat: "{{object_size}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Total Offline disks",
|
||||
Type: "singlestat",
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "sum(minio_disks_offline)",
|
||||
LegendFormat: "Total offline disks in MinIO Cluster",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Total Online Servers",
|
||||
Type: "stat",
|
||||
|
||||
Options: MetricOptions{
|
||||
ReduceOptions: ReduceOptions{
|
||||
Calcs: []string{
|
||||
"mean",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "count by (instances) (minio_version_info)",
|
||||
LegendFormat: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Total S3 Traffic Inbound",
|
||||
Type: "stat",
|
||||
|
||||
Options: MetricOptions{
|
||||
ReduceOptions: ReduceOptions{
|
||||
Calcs: []string{
|
||||
"mean",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "sum without (instance) (s3_rx_bytes_total)",
|
||||
LegendFormat: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Number of Buckets",
|
||||
Type: "stat",
|
||||
|
||||
Options: MetricOptions{
|
||||
ReduceOptions: ReduceOptions{
|
||||
Calcs: []string{
|
||||
"lastNotNull",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "count(count by (bucket) (bucket_objects_count))",
|
||||
LegendFormat: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "S3 API Request & Error Rate",
|
||||
Type: "graph",
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "sum without (instance,api)(rate(s3_requests_total[10m]))",
|
||||
LegendFormat: "S3 Requests",
|
||||
},
|
||||
{
|
||||
Expr: "sum without (instance,api)(rate(s3_errors_total[10m]))",
|
||||
LegendFormat: "S3 Errors",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Total Open FDs",
|
||||
Type: "stat",
|
||||
|
||||
Options: MetricOptions{
|
||||
ReduceOptions: ReduceOptions{
|
||||
Calcs: []string{
|
||||
"mean",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "sum without (instance)(process_open_fds)",
|
||||
LegendFormat: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Total S3 Traffic Outbound",
|
||||
Type: "stat",
|
||||
|
||||
Options: MetricOptions{
|
||||
ReduceOptions: ReduceOptions{
|
||||
Calcs: []string{
|
||||
"mean",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "sum without (instance)(s3_tx_bytes_total)",
|
||||
LegendFormat: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Number of Objects",
|
||||
Type: "stat",
|
||||
|
||||
Options: MetricOptions{
|
||||
ReduceOptions: ReduceOptions{
|
||||
Calcs: []string{
|
||||
"lastNotNull",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "topk(1, sum(bucket_objects_count) by (instance))",
|
||||
LegendFormat: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Total Goroutines",
|
||||
Type: "stat",
|
||||
|
||||
Options: MetricOptions{
|
||||
ReduceOptions: ReduceOptions{
|
||||
Calcs: []string{
|
||||
"mean",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "sum without (instance) (go_goroutines)",
|
||||
LegendFormat: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "S3 API Data Transfer",
|
||||
Type: "graph",
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "sum without (instance,api)(rate(s3_tx_bytes_total[5m]))",
|
||||
LegendFormat: "S3 Data Sent",
|
||||
},
|
||||
{
|
||||
Expr: "sum without (instance,api)(rate(s3_rx_bytes_total[5m]))",
|
||||
LegendFormat: "S3 Data Received",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Total S3 API Data Transfer",
|
||||
Type: "graph",
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "sum without (instance) (s3_rx_bytes_total)",
|
||||
LegendFormat: "S3 Bytes Received {{instance}}",
|
||||
},
|
||||
{
|
||||
Expr: "sum without (instance) (s3_tx_bytes_total)",
|
||||
LegendFormat: "S3 Bytes Sent {{instance}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Active S3 Requests",
|
||||
Type: "graph",
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "s3_requests_current{instance=~\"$instance\"}",
|
||||
LegendFormat: "Instance {{instance}} function {{api}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Internode Data Transfer",
|
||||
Type: "graph",
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "internode_rx_bytes_total{instance=~\"$instance\"}",
|
||||
LegendFormat: "Internode Bytes Received {{instance}}",
|
||||
},
|
||||
{
|
||||
Expr: "internode_tx_bytes_total{instance=~\"$instance\"}",
|
||||
LegendFormat: "Internode Bytes Sent {{instance}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Online Disks",
|
||||
Type: "graph",
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "minio_disks_total{instance=~\"$instance\"} - minio_disks_offline{instance=~\"$instance\"}",
|
||||
LegendFormat: "Online Disks {{instance}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Title: "Disk Usage",
|
||||
Type: "graph",
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: "disk_storage_used{disk=~\"$disk\",instance=~\"$instance\"}",
|
||||
LegendFormat: "Used Capacity {{instance}} {{disk}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type Widget struct {
|
||||
Title string
|
||||
Type string
|
||||
}
|
||||
|
||||
type DataResult struct {
|
||||
Metric map[string]string `json:"metric"`
|
||||
Values []interface{} `json:"values"`
|
||||
}
|
||||
|
||||
type PromRespData struct {
|
||||
ResultType string `json:"resultType"`
|
||||
Result []DataResult `json:"result"`
|
||||
}
|
||||
type PromResp struct {
|
||||
Status string `json:"status"`
|
||||
Data PromRespData `json:"data"`
|
||||
}
|
||||
|
||||
type LabelResponse struct {
|
||||
Status string `json:"status"`
|
||||
Data []string `json:"data"`
|
||||
}
|
||||
type LabelResults struct {
|
||||
Label string
|
||||
Response LabelResponse
|
||||
}
|
||||
|
||||
// getAdminInfoResponse returns the response containing total buckets, objects and usage.
|
||||
func getAdminInfoResponse(session *models.Principal) (*models.AdminInfoResponse, *models.Error) {
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
func getAdminInfoResponse(session *models.Principal, params admin_api.AdminInfoParams) (*models.AdminInfoResponse, *models.Error) {
|
||||
prometheusURL := getPrometheusURL()
|
||||
|
||||
if prometheusURL == "" {
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
// 20 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
// serialize output
|
||||
usage, err := getAdminInfo(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
sessionResp := &models.AdminInfoResponse{
|
||||
Buckets: usage.Buckets,
|
||||
Objects: usage.Objects,
|
||||
Usage: usage.Usage,
|
||||
}
|
||||
return sessionResp, nil
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
// 20 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
// serialize output
|
||||
usage, err := getAdminInfo(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
|
||||
labelResultsCh := make(chan LabelResults)
|
||||
|
||||
for _, lbl := range labels {
|
||||
go func(lbl WidgetLabel) {
|
||||
endpoint := fmt.Sprintf("%s/api/v1/label/%s/values", prometheusURL, lbl.Name)
|
||||
|
||||
resp, err := http.Get(endpoint)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Println(endpoint)
|
||||
log.Println(resp.StatusCode)
|
||||
log.Println(string(body))
|
||||
return
|
||||
}
|
||||
|
||||
var response LabelResponse
|
||||
jd := json.NewDecoder(resp.Body)
|
||||
if err = jd.Decode(&response); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
labelResultsCh <- LabelResults{Label: lbl.Name, Response: response}
|
||||
|
||||
}(lbl)
|
||||
}
|
||||
sessionResp := &models.AdminInfoResponse{
|
||||
Buckets: usage.Buckets,
|
||||
Objects: usage.Objects,
|
||||
Usage: usage.Usage,
|
||||
|
||||
labelMap := make(map[string][]string)
|
||||
|
||||
// wait for as many goroutines that come back in less than 1 second
|
||||
LabelsWaitLoop:
|
||||
for {
|
||||
select {
|
||||
case <-time.After(1 * time.Second):
|
||||
break LabelsWaitLoop
|
||||
case res := <-labelResultsCh:
|
||||
labelMap[res.Label] = res.Response.Data
|
||||
if len(labelMap) >= len(labels) {
|
||||
break LabelsWaitLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// launch a goroutines per widget
|
||||
|
||||
results := make(chan models.Widget)
|
||||
for _, m := range widgets {
|
||||
go func(m Metric, params admin_api.AdminInfoParams) {
|
||||
targetResults := make(chan *models.ResultTarget)
|
||||
// for each target we will launch another goroutine to fetch the values
|
||||
for _, target := range m.Targets {
|
||||
go func(target Target, params admin_api.AdminInfoParams) {
|
||||
apiType := "query_range"
|
||||
now := time.Now()
|
||||
extraParamters := fmt.Sprintf("&start=%d&end=%d&step=%d", now.Add(-15*time.Minute).Unix(), now.Unix(), *params.Step)
|
||||
|
||||
if params.Start != nil && params.End != nil {
|
||||
extraParamters = fmt.Sprintf("&start=%d&end=%d&step=%d", *params.Start, *params.End, *params.Step)
|
||||
}
|
||||
|
||||
queryExpr := target.Expr
|
||||
|
||||
if strings.Contains(queryExpr, "$") {
|
||||
var re = regexp.MustCompile(`\$([a-z]+)`)
|
||||
|
||||
for _, match := range re.FindAllStringSubmatch(queryExpr, -1) {
|
||||
if val, ok := labelMap[match[1]]; ok {
|
||||
queryExpr = strings.ReplaceAll(queryExpr, "$"+match[1], fmt.Sprintf("(%s)", strings.Join(val, "|")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
endpoint := fmt.Sprintf("%s/api/v1/%s?query=%s%s", getPrometheusURL(), apiType, url.QueryEscape(queryExpr), extraParamters)
|
||||
resp, err := http.Get(endpoint)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Println(endpoint)
|
||||
log.Println(resp.StatusCode)
|
||||
log.Println(string(body))
|
||||
return
|
||||
}
|
||||
|
||||
var response PromResp
|
||||
jd := json.NewDecoder(resp.Body)
|
||||
if err = jd.Decode(&response); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
//body, _ := ioutil.ReadAll(resp.Body)
|
||||
//err = json.Unmarshal(body, &response)
|
||||
//if err != nil {
|
||||
// log.Println(err)
|
||||
//}
|
||||
|
||||
targetResult := models.ResultTarget{
|
||||
LegendFormat: target.LegendFormat,
|
||||
ResultType: response.Data.ResultType,
|
||||
}
|
||||
for _, r := range response.Data.Result {
|
||||
targetResult.Result = append(targetResult.Result, &models.WidgetResult{
|
||||
Metric: r.Metric,
|
||||
Values: r.Values,
|
||||
})
|
||||
}
|
||||
|
||||
//xx, err := json.Marshal(response)
|
||||
//if err != nil {
|
||||
// log.Println(err)
|
||||
//}
|
||||
//log.Println("----", m.Title)
|
||||
//log.Println(string(body))
|
||||
//log.Println(string(xx))
|
||||
//log.Println("=====")
|
||||
|
||||
targetResults <- &targetResult
|
||||
|
||||
}(target, params)
|
||||
}
|
||||
|
||||
wdgtResult := models.Widget{
|
||||
Title: m.Title,
|
||||
Type: m.Type,
|
||||
}
|
||||
if len(m.Options.ReduceOptions.Calcs) > 0 {
|
||||
wdgtResult.Options = &models.WidgetOptions{
|
||||
ReduceOptions: &models.WidgetOptionsReduceOptions{
|
||||
Calcs: m.Options.ReduceOptions.Calcs,
|
||||
},
|
||||
}
|
||||
}
|
||||
// count how many targets we have received
|
||||
targetsReceived := 0
|
||||
|
||||
for res := range targetResults {
|
||||
wdgtResult.Targets = append(wdgtResult.Targets, res)
|
||||
targetsReceived++
|
||||
// upon receiving the total number of targets needed, we can close the channel to not lock the goroutine
|
||||
if targetsReceived >= len(m.Targets) {
|
||||
close(targetResults)
|
||||
}
|
||||
}
|
||||
|
||||
results <- wdgtResult
|
||||
}(m, params)
|
||||
}
|
||||
|
||||
// count the number of widgets that have completed calculating
|
||||
totalWidgets := 0
|
||||
sessionResp := &models.AdminInfoResponse{}
|
||||
|
||||
var wdgts []*models.Widget
|
||||
// wait for as many goroutines that come back in less than 1 second
|
||||
WaitLoop:
|
||||
for {
|
||||
select {
|
||||
case <-time.After(1 * time.Second):
|
||||
break WaitLoop
|
||||
case res := <-results:
|
||||
wdgts = append(wdgts, &res)
|
||||
totalWidgets++
|
||||
if totalWidgets >= len(widgets) {
|
||||
break WaitLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sessionResp.Widgets = wdgts
|
||||
|
||||
return sessionResp, nil
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
@@ -310,7 +311,7 @@ func getTenantInfo(tenant *operator.Tenant) *models.Tenant {
|
||||
}
|
||||
var deletion string
|
||||
if tenant.ObjectMeta.DeletionTimestamp != nil {
|
||||
deletion = tenant.ObjectMeta.DeletionTimestamp.String()
|
||||
deletion = tenant.ObjectMeta.DeletionTimestamp.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
if tenant.HasConsoleEnabled() {
|
||||
@@ -318,7 +319,7 @@ func getTenantInfo(tenant *operator.Tenant) *models.Tenant {
|
||||
}
|
||||
|
||||
return &models.Tenant{
|
||||
CreationDate: tenant.ObjectMeta.CreationTimestamp.String(),
|
||||
CreationDate: tenant.ObjectMeta.CreationTimestamp.Format(time.RFC3339),
|
||||
DeletionDate: deletion,
|
||||
Name: tenant.Name,
|
||||
TotalSize: totalSize,
|
||||
@@ -351,14 +352,16 @@ func getTenantInfoResponse(session *models.Principal, params admin_api.TenantInf
|
||||
|
||||
info := getTenantInfo(minTenant)
|
||||
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
|
||||
if minTenant.Spec.Console != nil {
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// obtain current subnet license for tenant (if exists)
|
||||
license, _ := getSubscriptionLicense(context.Background(), &k8sClient, params.Namespace, minTenant.Spec.Console.ConsoleSecret.Name)
|
||||
if license != "" {
|
||||
@@ -373,6 +376,40 @@ func getTenantInfoResponse(session *models.Principal, params admin_api.TenantInf
|
||||
}
|
||||
}
|
||||
|
||||
// get tenant service
|
||||
minTenant.EnsureDefaults()
|
||||
//minio service
|
||||
minSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.MinIOCIServiceName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
//console service
|
||||
conSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.ConsoleCIServiceName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
schema := "http"
|
||||
consolePort := ":9090"
|
||||
if minTenant.TLS() {
|
||||
schema = "https"
|
||||
consolePort = ":9443"
|
||||
}
|
||||
var minioEndpoint string
|
||||
var consoleEndpoint string
|
||||
if len(minSvc.Status.LoadBalancer.Ingress) > 0 {
|
||||
minioEndpoint = fmt.Sprintf("%s://%s", schema, minSvc.Status.LoadBalancer.Ingress[0].IP)
|
||||
}
|
||||
if len(conSvc.Status.LoadBalancer.Ingress) > 0 {
|
||||
consoleEndpoint = fmt.Sprintf("%s://%s%s", schema, conSvc.Status.LoadBalancer.Ingress[0].IP, consolePort)
|
||||
}
|
||||
if minioEndpoint != "" || consoleEndpoint != "" {
|
||||
info.Endpoints = &models.TenantEndpoints{
|
||||
Console: consoleEndpoint,
|
||||
Minio: minioEndpoint,
|
||||
}
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
@@ -407,11 +444,11 @@ func listTenants(ctx context.Context, operatorClient OperatorClientI, namespace
|
||||
|
||||
var deletion string
|
||||
if tenant.ObjectMeta.DeletionTimestamp != nil {
|
||||
deletion = tenant.ObjectMeta.DeletionTimestamp.String()
|
||||
deletion = tenant.ObjectMeta.DeletionTimestamp.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
tenants = append(tenants, &models.TenantList{
|
||||
CreationDate: tenant.ObjectMeta.CreationTimestamp.String(),
|
||||
CreationDate: tenant.ObjectMeta.CreationTimestamp.Format(time.RFC3339),
|
||||
DeletionDate: deletion,
|
||||
Name: tenant.ObjectMeta.Name,
|
||||
PoolCount: int64(len(tenant.Spec.Pools)),
|
||||
@@ -702,7 +739,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
|
||||
minInst.Spec.Console = &operator.ConsoleConfiguration{
|
||||
Replicas: 1,
|
||||
Image: ConsoleImageVersion,
|
||||
Image: getConsoleImage(),
|
||||
ConsoleSecret: &corev1.LocalObjectReference{Name: consoleSecretName},
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: map[corev1.ResourceName]resource.Quantity{
|
||||
@@ -808,6 +845,26 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
if tenantReq.ConsoleImage != "" {
|
||||
minInst.Spec.Console.Image = tenantReq.ConsoleImage
|
||||
}
|
||||
// default activate lgo search and prometheus
|
||||
minInst.Spec.Log = &operator.LogConfig{
|
||||
Image: "miniodev/logsearch:v4.0.0",
|
||||
Audit: &operator.AuditConfig{DiskCapacityGB: swag.Int(10)},
|
||||
}
|
||||
minInst.Spec.Prometheus = &operator.PrometheusConfig{
|
||||
DiskCapacityDB: swag.Int(5),
|
||||
}
|
||||
|
||||
// expose services
|
||||
if tenantReq.ExposeMinio || tenantReq.ExposeConsole {
|
||||
minInst.Spec.ExposeServices = &operator.ExposeServices{
|
||||
MinIO: tenantReq.ExposeMinio,
|
||||
Console: tenantReq.ExposeConsole,
|
||||
}
|
||||
log.Println("happened")
|
||||
}
|
||||
|
||||
yo, _ := yaml.Marshal(minInst)
|
||||
log.Println(string(yo))
|
||||
|
||||
opClient, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/cluster"
|
||||
@@ -325,7 +326,7 @@ func Test_TenantInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
want: &models.Tenant{
|
||||
CreationDate: testTimeStamp.String(),
|
||||
CreationDate: testTimeStamp.Format(time.RFC3339),
|
||||
Name: "tenant1",
|
||||
TotalSize: int64(8388608),
|
||||
CurrentState: "ready",
|
||||
@@ -389,8 +390,8 @@ func Test_TenantInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
want: &models.Tenant{
|
||||
CreationDate: testTimeStamp.String(),
|
||||
DeletionDate: testTimeStamp.String(),
|
||||
CreationDate: testTimeStamp.Format(time.RFC3339),
|
||||
DeletionDate: testTimeStamp.Format(time.RFC3339),
|
||||
Name: "tenant1",
|
||||
TotalSize: int64(8388608),
|
||||
CurrentState: "ready",
|
||||
@@ -435,7 +436,7 @@ func Test_TenantInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
want: &models.Tenant{
|
||||
CreationDate: testTimeStamp.String(),
|
||||
CreationDate: testTimeStamp.Format(time.RFC3339),
|
||||
Name: "tenant1",
|
||||
CurrentState: "ready",
|
||||
Namespace: "minio-ns",
|
||||
@@ -470,7 +471,7 @@ func Test_TenantInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
want: &models.Tenant{
|
||||
CreationDate: testTimeStamp.String(),
|
||||
CreationDate: testTimeStamp.Format(time.RFC3339),
|
||||
Name: "tenant1",
|
||||
CurrentState: "ready",
|
||||
Namespace: "minio-ns",
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/minio/minio/pkg/madmin"
|
||||
@@ -92,7 +93,7 @@ func shortTrace(info *madmin.ServiceTraceInfo) shortTraceMsg {
|
||||
t := info.Trace
|
||||
s := shortTraceMsg{}
|
||||
|
||||
s.Time = t.ReqInfo.Time.String()
|
||||
s.Time = t.ReqInfo.Time.Format(time.RFC3339)
|
||||
s.Path = t.ReqInfo.Path
|
||||
s.Query = t.ReqInfo.RawQuery
|
||||
s.FuncName = t.FuncName
|
||||
|
||||
@@ -19,8 +19,10 @@ package restapi
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio/pkg/certs"
|
||||
@@ -44,6 +46,13 @@ var TLSRedirect = "off"
|
||||
|
||||
var SessionDuration = 45 * time.Minute
|
||||
|
||||
var logSearchAPI string
|
||||
var logSearchURL string
|
||||
var prometheusURL string
|
||||
var consoleImage string
|
||||
|
||||
var once sync.Once
|
||||
|
||||
func getMinIOServer() string {
|
||||
return strings.TrimSpace(env.Get(ConsoleMinIOServer, "http://localhost:9000"))
|
||||
}
|
||||
@@ -220,11 +229,39 @@ func getSecureExpectCTHeader() string {
|
||||
return env.Get(ConsoleSecureExpectCTHeader, "")
|
||||
}
|
||||
|
||||
func getLogSearchAPIToken() string {
|
||||
once.Do(func() {
|
||||
initVars()
|
||||
})
|
||||
return logSearchAPI
|
||||
}
|
||||
|
||||
func getLogSearchURL() string {
|
||||
once.Do(func() {
|
||||
initVars()
|
||||
})
|
||||
return logSearchURL
|
||||
}
|
||||
|
||||
func getPrometheusURL() string {
|
||||
once.Do(func() {
|
||||
initVars()
|
||||
})
|
||||
return prometheusURL
|
||||
}
|
||||
|
||||
// GetSubnetLicense returns the current subnet jwt license
|
||||
func GetSubnetLicense() string {
|
||||
return env.Get(ConsoleSubnetLicense, "")
|
||||
}
|
||||
|
||||
func initVars() {
|
||||
logSearchAPI = env.Get(LogSearchQueryAuthToken, "")
|
||||
logSearchURL = env.Get(LogSearchURL, "http://localhost:8080")
|
||||
prometheusURL = env.Get(PrometheusURL, "")
|
||||
consoleImage = env.Get(ConsoleOperatorConsoleImage, ConsoleImageDefaultVersion)
|
||||
}
|
||||
|
||||
var (
|
||||
// GlobalRootCAs is CA root certificates, a nil value means system certs pool will be used
|
||||
GlobalRootCAs *x509.CertPool
|
||||
@@ -233,3 +270,20 @@ var (
|
||||
// GlobalTLSCertsManager custom TLS Manager for SNI support
|
||||
GlobalTLSCertsManager *certs.Manager
|
||||
)
|
||||
|
||||
// getK8sSAToken assumes the plugin is running inside a k8s pod and extract the current service account from the
|
||||
// /var/run/secrets/kubernetes.io/serviceaccount/token file
|
||||
func getK8sSAToken() string {
|
||||
dat, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
|
||||
if err != nil {
|
||||
return env.Get(ConsoleOperatorSAToken, "")
|
||||
}
|
||||
return string(dat)
|
||||
}
|
||||
|
||||
func getConsoleImage() string {
|
||||
once.Do(func() {
|
||||
initVars()
|
||||
})
|
||||
return consoleImage
|
||||
}
|
||||
|
||||
@@ -121,6 +121,8 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
|
||||
registerServiceAccountsHandlers(api)
|
||||
// Register admin remote buckets
|
||||
registerAdminBucketRemoteHandlers(api)
|
||||
// Register admin log search
|
||||
registerLogSearchHandlers(api)
|
||||
// Register admin subscription handlers
|
||||
registerSubscriptionHandlers(api)
|
||||
|
||||
|
||||
@@ -48,6 +48,11 @@ const (
|
||||
ConsoleSecureReferrerPolicy = "CONSOLE_SECURE_REFERRER_POLICY"
|
||||
ConsoleSecureFeaturePolicy = "CONSOLE_SECURE_FEATURE_POLICY"
|
||||
ConsoleSecureExpectCTHeader = "CONSOLE_SECURE_EXPECT_CT_HEADER"
|
||||
ConsoleOperatorSAToken = "CONSOLE_OPERATOR_SA_TOKEN"
|
||||
ConsoleOperatorConsoleImage = "CONSOLE_OPERATOR_CONSOLE_IMAGE"
|
||||
LogSearchURL = "CONSOLE_LOG_QUERY_URL"
|
||||
PrometheusURL = "CONSOLE_PROMETHEUS_URL"
|
||||
LogSearchQueryAuthToken = "LOGSEARCH_QUERY_AUTH_TOKEN"
|
||||
|
||||
// Constants for prometheus annotations
|
||||
prometheusPath = "prometheus.io/path"
|
||||
@@ -57,8 +62,8 @@ const (
|
||||
|
||||
// Image versions
|
||||
const (
|
||||
KESImageVersion = "minio/kes:v0.12.1"
|
||||
ConsoleImageVersion = "minio/console:v0.4.6"
|
||||
KESImageVersion = "minio/kes:v0.13.1"
|
||||
ConsoleImageDefaultVersion = "minio/console:v0.4.6"
|
||||
)
|
||||
|
||||
// K8s
|
||||
|
||||
@@ -115,6 +115,24 @@ func init() {
|
||||
],
|
||||
"summary": "Returns information about the deployment",
|
||||
"operationId": "AdminInfo",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "start",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "end",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"default": 15,
|
||||
"name": "step",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
@@ -1713,6 +1731,70 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/logs/search": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Search the logs",
|
||||
"operationId": "LogSearch",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"collectionFormat": "multi",
|
||||
"description": "Filter Parameters",
|
||||
"name": "fp",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"format": "int32",
|
||||
"default": 10,
|
||||
"name": "pageSize",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"format": "int32",
|
||||
"default": 0,
|
||||
"name": "pageNo",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"timeDesc",
|
||||
"timeAsc"
|
||||
],
|
||||
"type": "string",
|
||||
"default": "timeDesc",
|
||||
"name": "order",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "timeStart",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/logSearchResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/namespaces/{namespace}/resourcequotas/{resource-quota-name}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -3119,6 +3201,12 @@ func init() {
|
||||
},
|
||||
"usage": {
|
||||
"type": "integer"
|
||||
},
|
||||
"widgets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/widget"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -3517,6 +3605,12 @@ func init() {
|
||||
"erasureCodingParity": {
|
||||
"type": "integer"
|
||||
},
|
||||
"expose_console": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"expose_minio": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"idp": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/idpConfiguration"
|
||||
@@ -3985,6 +4079,15 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"logSearchResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"results": {
|
||||
"type": "object",
|
||||
"title": "list of log search responses"
|
||||
}
|
||||
}
|
||||
},
|
||||
"loginDetails": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -4838,6 +4941,23 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"resultTarget": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"legendFormat": {
|
||||
"type": "string"
|
||||
},
|
||||
"result": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/widgetResult"
|
||||
}
|
||||
},
|
||||
"resultType": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceAccountCreds": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -5071,6 +5191,17 @@ func init() {
|
||||
"enable_prometheus": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"endpoints": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"console": {
|
||||
"type": "string"
|
||||
},
|
||||
"minio": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -5312,6 +5443,54 @@ func init() {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"widget": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"reduceOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"calcs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"targets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/resultTarget"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"metric": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"values": {
|
||||
"type": "array",
|
||||
"items": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
@@ -5409,6 +5588,24 @@ func init() {
|
||||
],
|
||||
"summary": "Returns information about the deployment",
|
||||
"operationId": "AdminInfo",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "start",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "end",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"default": 15,
|
||||
"name": "step",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
@@ -7007,6 +7204,70 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/logs/search": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Search the logs",
|
||||
"operationId": "LogSearch",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"collectionFormat": "multi",
|
||||
"description": "Filter Parameters",
|
||||
"name": "fp",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"format": "int32",
|
||||
"default": 10,
|
||||
"name": "pageSize",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"format": "int32",
|
||||
"default": 0,
|
||||
"name": "pageNo",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"timeDesc",
|
||||
"timeAsc"
|
||||
],
|
||||
"type": "string",
|
||||
"default": "timeDesc",
|
||||
"name": "order",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "timeStart",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/logSearchResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/namespaces/{namespace}/resourcequotas/{resource-quota-name}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -8799,6 +9060,17 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"TenantEndpoints": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"console": {
|
||||
"type": "string"
|
||||
},
|
||||
"minio": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"VaultConfigurationApprole": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -8844,6 +9116,33 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"WidgetOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"reduceOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"calcs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"WidgetOptionsReduceOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"calcs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"accountChangePasswordRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -8936,6 +9235,12 @@ func init() {
|
||||
},
|
||||
"usage": {
|
||||
"type": "integer"
|
||||
},
|
||||
"widgets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/widget"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -9334,6 +9639,12 @@ func init() {
|
||||
"erasureCodingParity": {
|
||||
"type": "integer"
|
||||
},
|
||||
"expose_console": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"expose_minio": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"idp": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/idpConfiguration"
|
||||
@@ -9802,6 +10113,15 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"logSearchResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"results": {
|
||||
"type": "object",
|
||||
"title": "list of log search responses"
|
||||
}
|
||||
}
|
||||
},
|
||||
"loginDetails": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -10520,6 +10840,23 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"resultTarget": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"legendFormat": {
|
||||
"type": "string"
|
||||
},
|
||||
"result": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/widgetResult"
|
||||
}
|
||||
},
|
||||
"resultType": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceAccountCreds": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -10753,6 +11090,17 @@ func init() {
|
||||
"enable_prometheus": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"endpoints": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"console": {
|
||||
"type": "string"
|
||||
},
|
||||
"minio": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -10994,6 +11342,54 @@ func init() {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"widget": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"reduceOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"calcs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"targets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/resultTarget"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"metric": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"values": {
|
||||
"type": "array",
|
||||
"items": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
||||
@@ -26,14 +26,25 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// NewAdminInfoParams creates a new AdminInfoParams object
|
||||
// no default values defined in spec.
|
||||
// with the default values initialized.
|
||||
func NewAdminInfoParams() AdminInfoParams {
|
||||
|
||||
return AdminInfoParams{}
|
||||
var (
|
||||
// initialize parameters with default values
|
||||
|
||||
stepDefault = int64(15)
|
||||
)
|
||||
|
||||
return AdminInfoParams{
|
||||
Step: &stepDefault,
|
||||
}
|
||||
}
|
||||
|
||||
// AdminInfoParams contains all the bound params for the admin info operation
|
||||
@@ -44,6 +55,20 @@ type AdminInfoParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
In: query
|
||||
*/
|
||||
End *int64
|
||||
/*
|
||||
In: query
|
||||
*/
|
||||
Start *int64
|
||||
/*
|
||||
In: query
|
||||
Default: 15
|
||||
*/
|
||||
Step *int64
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
@@ -55,8 +80,92 @@ func (o *AdminInfoParams) BindRequest(r *http.Request, route *middleware.Matched
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
qs := runtime.Values(r.URL.Query())
|
||||
|
||||
qEnd, qhkEnd, _ := qs.GetOK("end")
|
||||
if err := o.bindEnd(qEnd, qhkEnd, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qStart, qhkStart, _ := qs.GetOK("start")
|
||||
if err := o.bindStart(qStart, qhkStart, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qStep, qhkStep, _ := qs.GetOK("step")
|
||||
if err := o.bindStep(qStep, qhkStep, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindEnd binds and validates parameter End from query.
|
||||
func (o *AdminInfoParams) bindEnd(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
if raw == "" { // empty values pass all other validations
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := swag.ConvertInt64(raw)
|
||||
if err != nil {
|
||||
return errors.InvalidType("end", "query", "int64", raw)
|
||||
}
|
||||
o.End = &value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindStart binds and validates parameter Start from query.
|
||||
func (o *AdminInfoParams) bindStart(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
if raw == "" { // empty values pass all other validations
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := swag.ConvertInt64(raw)
|
||||
if err != nil {
|
||||
return errors.InvalidType("start", "query", "int64", raw)
|
||||
}
|
||||
o.Start = &value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindStep binds and validates parameter Step from query.
|
||||
func (o *AdminInfoParams) bindStep(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
if raw == "" { // empty values pass all other validations
|
||||
// Default values have been previously initialized by NewAdminInfoParams()
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := swag.ConvertInt64(raw)
|
||||
if err != nil {
|
||||
return errors.InvalidType("step", "query", "int64", raw)
|
||||
}
|
||||
o.Step = &value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -26,11 +26,19 @@ import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// AdminInfoURL generates an URL for the admin info operation
|
||||
type AdminInfoURL struct {
|
||||
End *int64
|
||||
Start *int64
|
||||
Step *int64
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
@@ -60,6 +68,34 @@ func (o *AdminInfoURL) Build() (*url.URL, error) {
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
qs := make(url.Values)
|
||||
|
||||
var endQ string
|
||||
if o.End != nil {
|
||||
endQ = swag.FormatInt64(*o.End)
|
||||
}
|
||||
if endQ != "" {
|
||||
qs.Set("end", endQ)
|
||||
}
|
||||
|
||||
var startQ string
|
||||
if o.Start != nil {
|
||||
startQ = swag.FormatInt64(*o.Start)
|
||||
}
|
||||
if startQ != "" {
|
||||
qs.Set("start", startQ)
|
||||
}
|
||||
|
||||
var stepQ string
|
||||
if o.Step != nil {
|
||||
stepQ = swag.FormatInt64(*o.Step)
|
||||
}
|
||||
if stepQ != "" {
|
||||
qs.Set("step", stepQ)
|
||||
}
|
||||
|
||||
_result.RawQuery = qs.Encode()
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +208,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
|
||||
AdminAPIListUsersHandler: admin_api.ListUsersHandlerFunc(func(params admin_api.ListUsersParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.ListUsers has not yet been implemented")
|
||||
}),
|
||||
UserAPILogSearchHandler: user_api.LogSearchHandlerFunc(func(params user_api.LogSearchParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.LogSearch has not yet been implemented")
|
||||
}),
|
||||
UserAPILoginHandler: user_api.LoginHandlerFunc(func(params user_api.LoginParams) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.Login has not yet been implemented")
|
||||
}),
|
||||
@@ -473,6 +476,8 @@ type ConsoleAPI struct {
|
||||
UserAPIListUserServiceAccountsHandler user_api.ListUserServiceAccountsHandler
|
||||
// AdminAPIListUsersHandler sets the operation handler for the list users operation
|
||||
AdminAPIListUsersHandler admin_api.ListUsersHandler
|
||||
// UserAPILogSearchHandler sets the operation handler for the log search operation
|
||||
UserAPILogSearchHandler user_api.LogSearchHandler
|
||||
// UserAPILoginHandler sets the operation handler for the login operation
|
||||
UserAPILoginHandler user_api.LoginHandler
|
||||
// UserAPILoginDetailHandler sets the operation handler for the login detail operation
|
||||
@@ -771,6 +776,9 @@ func (o *ConsoleAPI) Validate() error {
|
||||
if o.AdminAPIListUsersHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.ListUsersHandler")
|
||||
}
|
||||
if o.UserAPILogSearchHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.LogSearchHandler")
|
||||
}
|
||||
if o.UserAPILoginHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.LoginHandler")
|
||||
}
|
||||
@@ -1182,6 +1190,10 @@ func (o *ConsoleAPI) initHandlerCache() {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/users"] = admin_api.NewListUsers(o.context, o.AdminAPIListUsersHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/logs/search"] = user_api.NewLogSearch(o.context, o.UserAPILogSearchHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
|
||||
90
restapi/operations/user_api/log_search.go
Normal file
90
restapi/operations/user_api/log_search.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// LogSearchHandlerFunc turns a function with the right signature into a log search handler
|
||||
type LogSearchHandlerFunc func(LogSearchParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn LogSearchHandlerFunc) Handle(params LogSearchParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// LogSearchHandler interface for that can handle valid log search params
|
||||
type LogSearchHandler interface {
|
||||
Handle(LogSearchParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewLogSearch creates a new http.Handler for the log search operation
|
||||
func NewLogSearch(ctx *middleware.Context, handler LogSearchHandler) *LogSearch {
|
||||
return &LogSearch{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*LogSearch swagger:route GET /logs/search UserAPI logSearch
|
||||
|
||||
Search the logs
|
||||
|
||||
*/
|
||||
type LogSearch struct {
|
||||
Context *middleware.Context
|
||||
Handler LogSearchHandler
|
||||
}
|
||||
|
||||
func (o *LogSearch) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
}
|
||||
var Params = NewLogSearchParams()
|
||||
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
r = aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
253
restapi/operations/user_api/log_search_parameters.go
Normal file
253
restapi/operations/user_api/log_search_parameters.go
Normal file
@@ -0,0 +1,253 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// NewLogSearchParams creates a new LogSearchParams object
|
||||
// with the default values initialized.
|
||||
func NewLogSearchParams() LogSearchParams {
|
||||
|
||||
var (
|
||||
// initialize parameters with default values
|
||||
|
||||
orderDefault = string("timeDesc")
|
||||
pageNoDefault = int32(0)
|
||||
pageSizeDefault = int32(10)
|
||||
)
|
||||
|
||||
return LogSearchParams{
|
||||
Order: &orderDefault,
|
||||
|
||||
PageNo: &pageNoDefault,
|
||||
|
||||
PageSize: &pageSizeDefault,
|
||||
}
|
||||
}
|
||||
|
||||
// LogSearchParams contains all the bound params for the log search operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters LogSearch
|
||||
type LogSearchParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*Filter Parameters
|
||||
In: query
|
||||
Collection Format: multi
|
||||
*/
|
||||
Fp []string
|
||||
/*
|
||||
In: query
|
||||
Default: "timeDesc"
|
||||
*/
|
||||
Order *string
|
||||
/*
|
||||
In: query
|
||||
Default: 0
|
||||
*/
|
||||
PageNo *int32
|
||||
/*
|
||||
In: query
|
||||
Default: 10
|
||||
*/
|
||||
PageSize *int32
|
||||
/*
|
||||
In: query
|
||||
*/
|
||||
TimeStart *string
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewLogSearchParams() beforehand.
|
||||
func (o *LogSearchParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
qs := runtime.Values(r.URL.Query())
|
||||
|
||||
qFp, qhkFp, _ := qs.GetOK("fp")
|
||||
if err := o.bindFp(qFp, qhkFp, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qOrder, qhkOrder, _ := qs.GetOK("order")
|
||||
if err := o.bindOrder(qOrder, qhkOrder, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qPageNo, qhkPageNo, _ := qs.GetOK("pageNo")
|
||||
if err := o.bindPageNo(qPageNo, qhkPageNo, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qPageSize, qhkPageSize, _ := qs.GetOK("pageSize")
|
||||
if err := o.bindPageSize(qPageSize, qhkPageSize, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qTimeStart, qhkTimeStart, _ := qs.GetOK("timeStart")
|
||||
if err := o.bindTimeStart(qTimeStart, qhkTimeStart, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindFp binds and validates array parameter Fp from query.
|
||||
//
|
||||
// Arrays are parsed according to CollectionFormat: "multi" (defaults to "csv" when empty).
|
||||
func (o *LogSearchParams) bindFp(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
|
||||
// CollectionFormat: multi
|
||||
fpIC := rawData
|
||||
|
||||
if len(fpIC) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var fpIR []string
|
||||
for _, fpIV := range fpIC {
|
||||
fpI := fpIV
|
||||
|
||||
fpIR = append(fpIR, fpI)
|
||||
}
|
||||
|
||||
o.Fp = fpIR
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindOrder binds and validates parameter Order from query.
|
||||
func (o *LogSearchParams) bindOrder(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
if raw == "" { // empty values pass all other validations
|
||||
// Default values have been previously initialized by NewLogSearchParams()
|
||||
return nil
|
||||
}
|
||||
|
||||
o.Order = &raw
|
||||
|
||||
if err := o.validateOrder(formats); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateOrder carries on validations for parameter Order
|
||||
func (o *LogSearchParams) validateOrder(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.EnumCase("order", "query", *o.Order, []interface{}{"timeDesc", "timeAsc"}, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindPageNo binds and validates parameter PageNo from query.
|
||||
func (o *LogSearchParams) bindPageNo(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
if raw == "" { // empty values pass all other validations
|
||||
// Default values have been previously initialized by NewLogSearchParams()
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := swag.ConvertInt32(raw)
|
||||
if err != nil {
|
||||
return errors.InvalidType("pageNo", "query", "int32", raw)
|
||||
}
|
||||
o.PageNo = &value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindPageSize binds and validates parameter PageSize from query.
|
||||
func (o *LogSearchParams) bindPageSize(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
if raw == "" { // empty values pass all other validations
|
||||
// Default values have been previously initialized by NewLogSearchParams()
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := swag.ConvertInt32(raw)
|
||||
if err != nil {
|
||||
return errors.InvalidType("pageSize", "query", "int32", raw)
|
||||
}
|
||||
o.PageSize = &value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindTimeStart binds and validates parameter TimeStart from query.
|
||||
func (o *LogSearchParams) bindTimeStart(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
if raw == "" { // empty values pass all other validations
|
||||
return nil
|
||||
}
|
||||
|
||||
o.TimeStart = &raw
|
||||
|
||||
return nil
|
||||
}
|
||||
133
restapi/operations/user_api/log_search_responses.go
Normal file
133
restapi/operations/user_api/log_search_responses.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// LogSearchOKCode is the HTTP code returned for type LogSearchOK
|
||||
const LogSearchOKCode int = 200
|
||||
|
||||
/*LogSearchOK A successful response.
|
||||
|
||||
swagger:response logSearchOK
|
||||
*/
|
||||
type LogSearchOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.LogSearchResponse `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewLogSearchOK creates LogSearchOK with default headers values
|
||||
func NewLogSearchOK() *LogSearchOK {
|
||||
|
||||
return &LogSearchOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the log search o k response
|
||||
func (o *LogSearchOK) WithPayload(payload *models.LogSearchResponse) *LogSearchOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the log search o k response
|
||||
func (o *LogSearchOK) SetPayload(payload *models.LogSearchResponse) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *LogSearchOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*LogSearchDefault Generic error response.
|
||||
|
||||
swagger:response logSearchDefault
|
||||
*/
|
||||
type LogSearchDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewLogSearchDefault creates LogSearchDefault with default headers values
|
||||
func NewLogSearchDefault(code int) *LogSearchDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &LogSearchDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the log search default response
|
||||
func (o *LogSearchDefault) WithStatusCode(code int) *LogSearchDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the log search default response
|
||||
func (o *LogSearchDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the log search default response
|
||||
func (o *LogSearchDefault) WithPayload(payload *models.Error) *LogSearchDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the log search default response
|
||||
func (o *LogSearchDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *LogSearchDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
164
restapi/operations/user_api/log_search_urlbuilder.go
Normal file
164
restapi/operations/user_api/log_search_urlbuilder.go
Normal file
@@ -0,0 +1,164 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// LogSearchURL generates an URL for the log search operation
|
||||
type LogSearchURL struct {
|
||||
Fp []string
|
||||
Order *string
|
||||
PageNo *int32
|
||||
PageSize *int32
|
||||
TimeStart *string
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *LogSearchURL) WithBasePath(bp string) *LogSearchURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *LogSearchURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *LogSearchURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/logs/search"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
qs := make(url.Values)
|
||||
|
||||
var fpIR []string
|
||||
for _, fpI := range o.Fp {
|
||||
fpIS := fpI
|
||||
if fpIS != "" {
|
||||
fpIR = append(fpIR, fpIS)
|
||||
}
|
||||
}
|
||||
|
||||
fp := swag.JoinByFormat(fpIR, "multi")
|
||||
|
||||
for _, qsv := range fp {
|
||||
qs.Add("fp", qsv)
|
||||
}
|
||||
|
||||
var orderQ string
|
||||
if o.Order != nil {
|
||||
orderQ = *o.Order
|
||||
}
|
||||
if orderQ != "" {
|
||||
qs.Set("order", orderQ)
|
||||
}
|
||||
|
||||
var pageNoQ string
|
||||
if o.PageNo != nil {
|
||||
pageNoQ = swag.FormatInt32(*o.PageNo)
|
||||
}
|
||||
if pageNoQ != "" {
|
||||
qs.Set("pageNo", pageNoQ)
|
||||
}
|
||||
|
||||
var pageSizeQ string
|
||||
if o.PageSize != nil {
|
||||
pageSizeQ = swag.FormatInt32(*o.PageSize)
|
||||
}
|
||||
if pageSizeQ != "" {
|
||||
qs.Set("pageSize", pageSizeQ)
|
||||
}
|
||||
|
||||
var timeStartQ string
|
||||
if o.TimeStart != nil {
|
||||
timeStartQ = *o.TimeStart
|
||||
}
|
||||
if timeStartQ != "" {
|
||||
qs.Set("timeStart", timeStartQ)
|
||||
}
|
||||
|
||||
_result.RawQuery = qs.Encode()
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *LogSearchURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *LogSearchURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *LogSearchURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on LogSearchURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on LogSearchURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *LogSearchURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -272,7 +272,7 @@ func getAccountInfo(ctx context.Context, client MinioAdmin) ([]*models.Bucket, e
|
||||
}
|
||||
var bucketInfos []*models.Bucket
|
||||
for _, bucket := range info.Buckets {
|
||||
bucketElem := &models.Bucket{Name: swag.String(bucket.Name), CreationDate: bucket.Created.String(), Size: int64(bucket.Size)}
|
||||
bucketElem := &models.Bucket{Name: swag.String(bucket.Name), CreationDate: bucket.Created.Format(time.RFC3339), Size: int64(bucket.Size)}
|
||||
bucketInfos = append(bucketInfos, bucketElem)
|
||||
}
|
||||
return bucketInfos, nil
|
||||
|
||||
@@ -128,7 +128,7 @@ func TestListBucket(t *testing.T) {
|
||||
assert.Equal(len(mockBucketList.Buckets), len(bucketList), fmt.Sprintf("Failed on %s: length of bucket's lists is not the same", function))
|
||||
for i, b := range bucketList {
|
||||
assert.Equal(mockBucketList.Buckets[i].Name, *b.Name)
|
||||
assert.Equal(mockBucketList.Buckets[i].Created.String(), b.CreationDate)
|
||||
assert.Equal(mockBucketList.Buckets[i].Created.Format(time.RFC3339), b.CreationDate)
|
||||
assert.Equal(mockBucketList.Buckets[i].Name, *b.Name)
|
||||
assert.Equal(int64(mockBucketList.Buckets[i].Size), b.Size)
|
||||
}
|
||||
|
||||
100
restapi/user_log_search.go
Normal file
100
restapi/user_log_search.go
Normal file
@@ -0,0 +1,100 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 restapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/user_api"
|
||||
logsearchServer "github.com/minio/operator/logsearchapi/server"
|
||||
)
|
||||
|
||||
func registerLogSearchHandlers(api *operations.ConsoleAPI) {
|
||||
// log search
|
||||
api.UserAPILogSearchHandler = user_api.LogSearchHandlerFunc(func(params user_api.LogSearchParams, session *models.Principal) middleware.Responder {
|
||||
searchResp, err := getLogSearchResponse(params)
|
||||
if err != nil {
|
||||
return user_api.NewLogSearchDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewLogSearchOK().WithPayload(searchResp)
|
||||
})
|
||||
}
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
}
|
||||
|
||||
// getLogSearchResponse performs a query to Log Search if Enabled
|
||||
func getLogSearchResponse(params user_api.LogSearchParams) (*models.LogSearchResponse, *models.Error) {
|
||||
token := getLogSearchAPIToken()
|
||||
endpoint := fmt.Sprintf("%s/api/query?token=%s&q=reqinfo", getLogSearchURL(), token)
|
||||
for _, fp := range params.Fp {
|
||||
endpoint = fmt.Sprintf("%s&fp=%s", endpoint, fp)
|
||||
}
|
||||
|
||||
endpoint = fmt.Sprintf("%s&%s=ok", endpoint, *params.Order)
|
||||
|
||||
// timeStart
|
||||
if params.TimeStart != nil && *params.TimeStart != "" {
|
||||
endpoint = fmt.Sprintf("%s&timeStart=%s", endpoint, *params.TimeStart)
|
||||
}
|
||||
// page size and page number
|
||||
endpoint = fmt.Sprintf("%s&pageSize=%d", endpoint, *params.PageSize)
|
||||
endpoint = fmt.Sprintf("%s&pageNo=%d", endpoint, *params.PageNo)
|
||||
|
||||
return logSearch(endpoint)
|
||||
}
|
||||
|
||||
func logSearch(endpoint string) (*models.LogSearchResponse, *models.Error) {
|
||||
resp, err := http.Get(endpoint)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
log.Println("Error Status Code", resp.StatusCode)
|
||||
_, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
return nil, &models.Error{
|
||||
Code: 500,
|
||||
Message: swag.String("Error retrieving logs"),
|
||||
}
|
||||
}
|
||||
|
||||
var results []logsearchServer.ReqInfoRow
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&results); err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
response := models.LogSearchResponse{
|
||||
Results: results,
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
129
restapi/user_log_search_test.go
Normal file
129
restapi/user_log_search_test.go
Normal file
@@ -0,0 +1,129 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 restapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
logsearchServer "github.com/minio/operator/logsearchapi/server"
|
||||
asrt "github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLogSearch(t *testing.T) {
|
||||
responseItem := []logsearchServer.ReqInfoRow{
|
||||
{
|
||||
Time: time.Time{},
|
||||
APIName: "GetConfigKV",
|
||||
Bucket: "",
|
||||
Object: "",
|
||||
TimeToResponseNs: 45254653,
|
||||
RemoteHost: "10.116.1.94",
|
||||
RequestID: "16595A4E30CCFE79",
|
||||
UserAgent: "MinIO (linux; amd64) madmin-go/0.0.1",
|
||||
ResponseStatus: "OK",
|
||||
ResponseStatusCode: 200,
|
||||
RequestContentLength: nil,
|
||||
ResponseContentLength: nil,
|
||||
}, {
|
||||
Time: time.Time{},
|
||||
APIName: "AssumeRole",
|
||||
Bucket: "",
|
||||
Object: "",
|
||||
TimeToResponseNs: 307423794,
|
||||
RemoteHost: "127.0.0.1",
|
||||
RequestID: "16595A4DA906FBA9",
|
||||
UserAgent: "Go-http-client/1.1",
|
||||
ResponseStatus: "OK",
|
||||
ResponseStatusCode: 200,
|
||||
RequestContentLength: nil,
|
||||
ResponseContentLength: nil,
|
||||
},
|
||||
}
|
||||
|
||||
assert := asrt.New(t)
|
||||
type args struct {
|
||||
apiResponse string
|
||||
apiResponseCode int
|
||||
}
|
||||
|
||||
response, _ := json.Marshal(responseItem)
|
||||
|
||||
successfulResponse := &models.LogSearchResponse{
|
||||
Results: responseItem,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedResponse *models.LogSearchResponse
|
||||
expectedError *models.Error
|
||||
}{
|
||||
{
|
||||
name: "200 Success response",
|
||||
args: args{
|
||||
apiResponse: fmt.Sprintf("%s\n", response),
|
||||
apiResponseCode: 200,
|
||||
},
|
||||
expectedResponse: successfulResponse,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "500 unsuccessful response",
|
||||
args: args{
|
||||
apiResponse: "Some random error",
|
||||
apiResponseCode: 500,
|
||||
},
|
||||
expectedResponse: nil,
|
||||
expectedError: &models.Error{
|
||||
Code: 500,
|
||||
Message: swag.String("Error retrieving logs"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
testRequest := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(tt.args.apiResponseCode)
|
||||
fmt.Fprintln(w, tt.args.apiResponse)
|
||||
}))
|
||||
defer testRequest.Close()
|
||||
|
||||
resp, err := logSearch(testRequest.URL)
|
||||
|
||||
if tt.expectedError != nil {
|
||||
fmt.Println(t.Name())
|
||||
assert.Equal(tt.expectedError.Code, err.Code, fmt.Sprintf("logSearch() error code: `%v`, wantErr: `%v`", err.Code, tt.expectedError))
|
||||
assert.Equal(tt.expectedError.Message, err.Message, fmt.Sprintf("logSearch() error message: `%v`, wantErr: `%v`", err.Message, tt.expectedError))
|
||||
} else {
|
||||
assert.Nil(err, fmt.Sprintf("logSearch() error: %v, wantErr: %v", err, tt.expectedError))
|
||||
if !reflect.DeepEqual(resp, tt.expectedResponse) {
|
||||
t.Errorf("logSearch() resp: %v, expectedResponse: %v", resp, tt.expectedResponse)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -176,9 +176,8 @@ func getLoginDetailsResponse() (*models.LoginDetails, *models.Error) {
|
||||
defer cancel()
|
||||
loginStrategy := models.LoginDetailsLoginStrategyForm
|
||||
redirectURL := ""
|
||||
if acl.GetOperatorMode() {
|
||||
loginStrategy = models.LoginDetailsLoginStrategyServiceAccount
|
||||
} else if oauth2.IsIdpEnabled() {
|
||||
|
||||
if oauth2.IsIdpEnabled() {
|
||||
loginStrategy = models.LoginDetailsLoginStrategyRedirect
|
||||
// initialize new oauth2 client
|
||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(ctx, nil)
|
||||
@@ -188,7 +187,10 @@ func getLoginDetailsResponse() (*models.LoginDetails, *models.Error) {
|
||||
// Validate user against IDP
|
||||
identityProvider := &auth.IdentityProvider{Client: oauth2Client}
|
||||
redirectURL = identityProvider.GenerateLoginURL()
|
||||
} else if acl.GetOperatorMode() {
|
||||
loginStrategy = models.LoginDetailsLoginStrategyServiceAccount
|
||||
}
|
||||
|
||||
loginDetails := &models.LoginDetails{
|
||||
LoginStrategy: loginStrategy,
|
||||
Redirect: redirectURL,
|
||||
@@ -209,7 +211,22 @@ func verifyUserAgainstIDP(ctx context.Context, provider auth.IdentityProviderI,
|
||||
func getLoginOauth2AuthResponse(lr *models.LoginOauth2AuthRequest) (*models.LoginResponse, *models.Error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
if oauth2.IsIdpEnabled() {
|
||||
if acl.GetOperatorMode() {
|
||||
creds, err := newConsoleCredentials("", getK8sSAToken(), "")
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
credentials := consoleCredentials{consoleCredentials: creds, actions: []string{}}
|
||||
token, err := login(credentials)
|
||||
if err != nil {
|
||||
return nil, prepareError(errInvalidCredentials, nil, err)
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
SessionID: *token,
|
||||
}
|
||||
return loginResponse, nil
|
||||
} else if oauth2.IsIdpEnabled() {
|
||||
// initialize new oauth2 client
|
||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(ctx, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -173,7 +173,7 @@ func listBucketObjects(ctx context.Context, client MinioClient, bucketName strin
|
||||
obj := &models.BucketObject{
|
||||
Name: lsObj.Key,
|
||||
Size: lsObj.Size,
|
||||
LastModified: lsObj.LastModified.String(),
|
||||
LastModified: lsObj.LastModified.Format(time.RFC3339),
|
||||
ContentType: lsObj.ContentType,
|
||||
VersionID: lsObj.VersionID,
|
||||
IsLatest: lsObj.IsLatest,
|
||||
@@ -205,7 +205,7 @@ func listBucketObjects(ctx context.Context, client MinioClient, bucketName strin
|
||||
if retention != nil && retUntilDate != nil {
|
||||
date := *retUntilDate
|
||||
obj.RetentionMode = string(*retention)
|
||||
obj.RetentionUntilDate = date.String()
|
||||
obj.RetentionUntilDate = date.Format(time.RFC3339)
|
||||
}
|
||||
}
|
||||
tags, err := client.getObjectTagging(ctx, bucketName, lsObj.Key, minio.GetObjectTaggingOptions{VersionID: lsObj.VersionID})
|
||||
|
||||
@@ -170,23 +170,23 @@ func Test_listObjects(t *testing.T) {
|
||||
expectedResp: []*models.BucketObject{
|
||||
&models.BucketObject{
|
||||
Name: "obj1",
|
||||
LastModified: t1.String(),
|
||||
LastModified: t1.Format(time.RFC3339),
|
||||
Size: int64(1024),
|
||||
ContentType: "content",
|
||||
LegalHoldStatus: string(minio.LegalHoldEnabled),
|
||||
RetentionMode: string(minio.Governance),
|
||||
RetentionUntilDate: tretention.String(),
|
||||
RetentionUntilDate: tretention.Format(time.RFC3339),
|
||||
Tags: map[string]string{
|
||||
"tag1": "value1",
|
||||
},
|
||||
}, &models.BucketObject{
|
||||
Name: "obj2",
|
||||
LastModified: t1.String(),
|
||||
LastModified: t1.Format(time.RFC3339),
|
||||
Size: int64(512),
|
||||
ContentType: "content",
|
||||
LegalHoldStatus: string(minio.LegalHoldEnabled),
|
||||
RetentionMode: string(minio.Governance),
|
||||
RetentionUntilDate: tretention.String(),
|
||||
RetentionUntilDate: tretention.Format(time.RFC3339),
|
||||
Tags: map[string]string{
|
||||
"tag1": "value1",
|
||||
},
|
||||
@@ -332,18 +332,18 @@ func Test_listObjects(t *testing.T) {
|
||||
expectedResp: []*models.BucketObject{
|
||||
&models.BucketObject{
|
||||
Name: "obj1",
|
||||
LastModified: t1.String(),
|
||||
LastModified: t1.Format(time.RFC3339),
|
||||
Size: int64(1024),
|
||||
ContentType: "content",
|
||||
IsDeleteMarker: true,
|
||||
}, &models.BucketObject{
|
||||
Name: "obj2",
|
||||
LastModified: t1.String(),
|
||||
LastModified: t1.Format(time.RFC3339),
|
||||
Size: int64(512),
|
||||
ContentType: "content",
|
||||
LegalHoldStatus: string(minio.LegalHoldEnabled),
|
||||
RetentionMode: string(minio.Governance),
|
||||
RetentionUntilDate: tretention.String(),
|
||||
RetentionUntilDate: tretention.Format(time.RFC3339),
|
||||
Tags: map[string]string{
|
||||
"tag1": "value1",
|
||||
},
|
||||
@@ -391,7 +391,7 @@ func Test_listObjects(t *testing.T) {
|
||||
expectedResp: []*models.BucketObject{
|
||||
&models.BucketObject{
|
||||
Name: "obj1",
|
||||
LastModified: t1.String(),
|
||||
LastModified: t1.Format(time.RFC3339),
|
||||
Size: int64(1024),
|
||||
ContentType: "content",
|
||||
},
|
||||
@@ -453,12 +453,12 @@ func Test_listObjects(t *testing.T) {
|
||||
expectedResp: []*models.BucketObject{
|
||||
&models.BucketObject{
|
||||
Name: "obj1",
|
||||
LastModified: t1.String(),
|
||||
LastModified: t1.Format(time.RFC3339),
|
||||
Size: int64(1024),
|
||||
ContentType: "content",
|
||||
}, &models.BucketObject{
|
||||
Name: "obj2",
|
||||
LastModified: t1.String(),
|
||||
LastModified: t1.Format(time.RFC3339),
|
||||
Size: int64(512),
|
||||
ContentType: "content",
|
||||
},
|
||||
@@ -520,12 +520,12 @@ func Test_listObjects(t *testing.T) {
|
||||
expectedResp: []*models.BucketObject{
|
||||
&models.BucketObject{
|
||||
Name: "obj1",
|
||||
LastModified: t1.String(),
|
||||
LastModified: t1.Format(time.RFC3339),
|
||||
Size: int64(1024),
|
||||
ContentType: "content",
|
||||
}, &models.BucketObject{
|
||||
Name: "obj2",
|
||||
LastModified: t1.String(),
|
||||
LastModified: t1.Format(time.RFC3339),
|
||||
Size: int64(512),
|
||||
ContentType: "content",
|
||||
},
|
||||
|
||||
132
swagger.yml
132
swagger.yml
@@ -19,7 +19,7 @@ securityDefinitions:
|
||||
tokenUrl: http://min.io
|
||||
# Apply the key security definition to all APIs
|
||||
security:
|
||||
- key: []
|
||||
- key: [ ]
|
||||
paths:
|
||||
/login:
|
||||
get:
|
||||
@@ -35,7 +35,7 @@ paths:
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
# Exclude this API from the authentication requirement
|
||||
security: []
|
||||
security: [ ]
|
||||
tags:
|
||||
- UserAPI
|
||||
post:
|
||||
@@ -57,7 +57,7 @@ paths:
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
# Exclude this API from the authentication requirement
|
||||
security: []
|
||||
security: [ ]
|
||||
tags:
|
||||
- UserAPI
|
||||
/login/operator:
|
||||
@@ -79,7 +79,7 @@ paths:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
security: []
|
||||
security: [ ]
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
@@ -102,7 +102,7 @@ paths:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
security: []
|
||||
security: [ ]
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
@@ -1516,6 +1516,17 @@ paths:
|
||||
get:
|
||||
summary: Returns information about the deployment
|
||||
operationId: AdminInfo
|
||||
parameters:
|
||||
- name: start
|
||||
in: query
|
||||
type: integer
|
||||
- name: end
|
||||
in: query
|
||||
type: integer
|
||||
- name: step
|
||||
default: 15
|
||||
in: query
|
||||
type: integer
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
@@ -1656,7 +1667,47 @@ paths:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
/logs/search:
|
||||
get:
|
||||
summary: Search the logs
|
||||
operationId: LogSearch
|
||||
parameters:
|
||||
- name: fp
|
||||
description: Filter Parameters
|
||||
in: query
|
||||
collectionFormat: multi
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
- name: pageSize
|
||||
in: query
|
||||
type: number
|
||||
format: int32
|
||||
default: 10
|
||||
- name: pageNo
|
||||
in: query
|
||||
type: number
|
||||
format: int32
|
||||
default: 0
|
||||
- name: order
|
||||
in: query
|
||||
type: string
|
||||
enum: [ timeDesc, timeAsc ]
|
||||
default: timeDesc
|
||||
- name: timeStart
|
||||
in: query
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
schema:
|
||||
$ref: "#/definitions/logSearchResponse"
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- UserAPI
|
||||
/tenants:
|
||||
get:
|
||||
summary: List Tenant of All Namespaces
|
||||
@@ -2515,7 +2566,7 @@ definitions:
|
||||
properties:
|
||||
loginStrategy:
|
||||
type: string
|
||||
enum: [form, redirect, service-account]
|
||||
enum: [ form, redirect, service-account ]
|
||||
redirect:
|
||||
type: string
|
||||
loginOauth2AuthRequest:
|
||||
@@ -2617,6 +2668,48 @@ definitions:
|
||||
enum: [ok]
|
||||
operator:
|
||||
type: boolean
|
||||
widgetResult:
|
||||
type: object
|
||||
properties:
|
||||
metric:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
values:
|
||||
type: array
|
||||
items: { }
|
||||
resultTarget:
|
||||
type: object
|
||||
properties:
|
||||
legendFormat:
|
||||
type: string
|
||||
resultType:
|
||||
type: string
|
||||
result:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/widgetResult"
|
||||
widget:
|
||||
type: object
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
options:
|
||||
type: object
|
||||
properties:
|
||||
reduceOptions:
|
||||
type: object
|
||||
properties:
|
||||
calcs:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
targets:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/resultTarget"
|
||||
adminInfoResponse:
|
||||
type: object
|
||||
properties:
|
||||
@@ -2626,6 +2719,10 @@ definitions:
|
||||
type: integer
|
||||
usage:
|
||||
type: integer
|
||||
widgets:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/widget"
|
||||
arnsResponse:
|
||||
type: object
|
||||
properties:
|
||||
@@ -2771,7 +2868,7 @@ definitions:
|
||||
type: string
|
||||
service:
|
||||
type: string
|
||||
enum: [replication]
|
||||
enum: [ replication ]
|
||||
createRemoteBucket:
|
||||
required:
|
||||
- accessKey
|
||||
@@ -2845,6 +2942,14 @@ definitions:
|
||||
type: boolean
|
||||
subnet_license:
|
||||
$ref: "#/definitions/license"
|
||||
endpoints:
|
||||
type: object
|
||||
properties:
|
||||
minio:
|
||||
type: string
|
||||
console:
|
||||
type: string
|
||||
|
||||
tenantUsage:
|
||||
type: object
|
||||
properties:
|
||||
@@ -2877,6 +2982,13 @@ definitions:
|
||||
namespace:
|
||||
type: string
|
||||
|
||||
logSearchResponse:
|
||||
type: object
|
||||
properties:
|
||||
results:
|
||||
type: object
|
||||
title: list of log search responses
|
||||
|
||||
listTenantsResponse:
|
||||
type: object
|
||||
properties:
|
||||
@@ -2981,6 +3093,10 @@ definitions:
|
||||
console:
|
||||
type: object
|
||||
$ref: "#/definitions/consoleConfiguration"
|
||||
expose_minio:
|
||||
type: boolean
|
||||
expose_console:
|
||||
type: boolean
|
||||
|
||||
metadataFields:
|
||||
type: object
|
||||
|
||||
Reference in New Issue
Block a user