Compare commits

..

34 Commits

Author SHA1 Message Date
Minio Trusted
29e1af3363 update to v0.7.0 2021-05-05 11:24:26 -07:00
Daniel Valdivia
8a635fc0d7 Make Log Search and Prometheus images configurable (#717)
Three new fields were added to let users customize the docker images for log search and prometheus

Signed-off-by: Daniel Valdivia <hola@danielvaldivia.com>
2021-05-04 14:46:10 -07:00
Alex
35da684ec9 Added direct-csi API support to console (#710) 2021-05-04 11:56:33 -05:00
Daniel Valdivia
8cfa41eb53 Make Tabs Scrollable (#714)
This makes so tab components are scrollable if the resolution on the screen demands it

Signed-off-by: Daniel Valdivia <hola@danielvaldivia.com>
2021-05-03 13:19:35 -07:00
Alex
08e40fe632 Fixed issue with namespace not returning error (#709)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>

Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2021-05-03 08:38:20 -07:00
Alex
0b0cb785b5 Fixed small issues & removed warnings in console (#711) 2021-04-27 21:55:16 -07:00
Daniel Valdivia
5838606789 Improvements to Built-In users (#708) 2021-04-27 13:07:25 -07:00
Alex
38f95e3b28 Added affinity support to tenant add screen (#706) 2021-04-26 18:40:09 -07:00
Daniel Valdivia
0c90785ed0 Remove printing of tenant yaml (#705)
Signed-off-by: Daniel Valdivia <hola@danielvaldivia.com>

Co-authored-by: Alex <33497058+bexsoft@users.noreply.github.com>
2021-04-26 10:35:45 -07:00
Alex
62d762318f Tiers & lifecycle implementation (#638) 2021-04-24 14:31:47 -07:00
adfost
097e6e9825 changing delete button (#704)
Co-authored-by: Lenin Alevski <alevsk.8772@gmail.com>
2021-04-23 15:56:06 -07:00
Alex
9e35db0642 Added validation for single tenant in namespace (#701)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>

Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2021-04-23 13:38:02 -07:00
Lenin Alevski
3b55d63211 Attaching subnet license to existing tenants fails (#702)
Uses the right tenant namespace to update console secrets instead of the
wrong operator namespace

Signed-off-by: Lenin Alevski <alevsk.8772@gmail.com>
2021-04-23 12:58:37 -07:00
Alex
2981e63797 Fixes an issue with empty logsearch & prometheus configurations (#703) 2021-04-22 20:54:17 -07:00
adfost
ca742b781f Adding key creation in tenant wizard. (#697)
Signed-off-by: Adam Stafford <adam@minio.io>

Co-authored-by: Adam Stafford <adamstafford@MacBook-Pro-van-Adam-2.local>
2021-04-22 14:18:14 -07:00
dependabot[bot]
d60cac0122 Bump ssri from 6.0.1 to 6.0.2 in /portal-ui (#698)
Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alex <33497058+bexsoft@users.noreply.github.com>
2021-04-20 23:59:09 -07:00
Alex
62745e9ff2 Added log search & prometheus extra params configuration (#700) 2021-04-20 23:39:14 -07:00
Alex
c81da22e8a Added storage class selector to add pool modal (#699)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2021-04-19 18:26:22 -07:00
Minio Trusted
c07b7b4772 update to v0.6.8 2021-04-19 14:42:47 -07:00
Minio Trusted
6cb703685b update to v0.6.7 2021-04-14 14:26:09 -07:00
Alex
7a3c2a3cf8 Disabled selected buttons in table wrapper (#692)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2021-04-14 14:20:30 -07:00
Alex
d8ac8cc1e3 Merged object locking & access policy cards into a single one for bucket details (#691)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2021-04-14 12:52:29 -07:00
Daniel Valdivia
0e492dae67 Add Restruct with policy selector in add SA modal (#689) 2021-04-13 16:16:53 -07:00
Alex
b72217764a Removed center alignment of forms in full screen (#690)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2021-04-13 15:51:39 -07:00
Lenin Alevski
26bcbb896a Remove unecessary default scopes for IDP integration (#686) 2021-04-08 14:04:32 -07:00
Lenin Alevski
7b4254f525 Display errors during IDP authentication (#685) 2021-04-06 00:38:11 -07:00
Alex
68ea3b5d05 Fixed double scroll issue in modal wizard (#684)
Also changed grid classes for wizard/ modal-wizard
2021-04-05 20:31:00 -05:00
Alex
8a51570d14 Added replication bulk modal to buckets list (#665)
* Added replication bulk modal to buckets list

* Disabled buttons & inputs in loading state

Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2021-04-05 16:20:08 -07:00
Daniel Valdivia
9c6ed83d63 make tenant breadcrum clickable (#683) 2021-04-02 17:04:55 -07:00
adfost
4f29f8457f Api bucket policy (#674)
* Adding API for Users with Access to Bucket

* changing error logging

* Delete .yarn-integrity
2021-04-02 17:13:29 -06:00
Lenin Alevski
94747acab2 fixes issue about displaying documentation link multiple times (#681) 2021-04-01 23:02:45 -07:00
Alex
d8360737ee Fixed max width in wizard panels & issue in wide screens for input switchers (#679)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2021-04-01 18:35:33 -07:00
Daniel Valdivia
bb91cb66b0 Operator UI improvements (#678) 2021-04-01 16:13:57 -07:00
Lenin Alevski
ce473b9286 Adding references for operator and console links in license page (#675) 2021-03-30 17:01:56 -07:00
186 changed files with 16291 additions and 927 deletions

13
go.mod
View File

@@ -18,19 +18,20 @@ require (
github.com/minio/cli v1.22.0
github.com/minio/direct-csi v1.2.8
github.com/minio/kes v0.11.0
github.com/minio/mc v0.0.0-20210301162250-f9d36f9b5243
github.com/minio/minio v0.0.0-20210301203133-e8d8dfa3ae8f
github.com/minio/minio-go/v7 v7.0.10
github.com/minio/operator v0.0.0-20210317030027-207337abe7fd
github.com/minio/mc v0.0.0-20210422171734-4eae7ec7ed25
github.com/minio/minio v0.0.0-20210423185853-cbfdf97abf9f
github.com/minio/minio-go/v7 v7.0.11-0.20210407221404-ba867dba7ee1
github.com/minio/operator v0.0.0-20210419212754-93a9239fd18b
github.com/minio/operator/logsearchapi v0.0.0-20210201110528-753019b838b4
github.com/minio/selfupdate v0.3.1
github.com/mitchellh/go-homedir v1.1.0
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/rs/xid v1.2.1
github.com/secure-io/sio-go v0.3.1
github.com/stretchr/testify v1.6.1
github.com/unrolled/secure v1.0.7
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392
golang.org/x/net v0.0.0-20201216054612-986b41b23924
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
gopkg.in/yaml.v2 v2.3.0
k8s.io/api v0.20.2

113
go.sum
View File

@@ -9,6 +9,7 @@ cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
@@ -55,6 +56,7 @@ github.com/Azure/go-autorest v14.1.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
github.com/Azure/go-autorest/autorest v0.10.2/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
@@ -119,6 +121,7 @@ github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdc
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
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/go.mod h1:SW6HZGeZgSIpcUWX3fXpfZhuaWHnmoD5KCVaqSaNTkk=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -126,6 +129,7 @@ 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/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=
@@ -375,6 +379,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=
@@ -410,6 +415,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=
@@ -468,8 +474,9 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -536,8 +543,9 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-containerregistry v0.1.2/go.mod h1:GPivBPgdAyd2SU+vf6EpsgOtWDuPqjW0hJZt4rNdTZ4=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@@ -568,6 +576,7 @@ github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
@@ -788,6 +797,7 @@ github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaR
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
@@ -795,8 +805,9 @@ github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0
github.com/klauspost/compress v1.10.1/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/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.12 h1:famVnQVu7QwryBN4jNseQdUKES71ZAOnB6UQQJPZvqk=
github.com/klauspost/compress v1.11.12/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.2/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
@@ -833,12 +844,15 @@ 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.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g=
github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -899,26 +913,27 @@ github.com/minio/controller-tools v0.4.7/go.mod h1:xES4iNis9dGrLQuP6nquTZZNg2T0/
github.com/minio/direct-csi v1.2.8 h1:jOpefwTGZYUIzouz5McSQ8EqeXO7Qu5aCJIwstbc6vs=
github.com/minio/direct-csi v1.2.8/go.mod h1:+Zw8NjMQ5rQqySezan6G9DsQjHSHQCPWJli3v9dP80o=
github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc=
github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0=
github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
github.com/minio/kes v0.11.0 h1:8ma6OCVSxKT50b1uYXLJro3m7PmZtCLxBaTddQexI5k=
github.com/minio/kes v0.11.0/go.mod h1:mTF1Bv8YVEtQqF/B7Felp4tLee44Pp+dgI0rhCvgNg8=
github.com/minio/mc v0.0.0-20210301162250-f9d36f9b5243 h1:V0EoJ/I/p86J8FH2zuOSTsTzNzDXQX4xZKvBwBLS/Qk=
github.com/minio/mc v0.0.0-20210301162250-f9d36f9b5243/go.mod h1:nkHp/atLUKkhML5YGfvaDDFqlcBmuii7s9Dbk3ulB1Q=
github.com/minio/mc v0.0.0-20210422171734-4eae7ec7ed25 h1:HrVywZfBwMUS6fQMdaOgyD5XJEMnwd7fAaR99oy5mow=
github.com/minio/mc v0.0.0-20210422171734-4eae7ec7ed25/go.mod h1:0mEfTTrQEZJXPvi9dNQWRlRplsmSmH+Z5h6G/2N3F/0=
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
github.com/minio/md5-simd v1.1.1 h1:9ojcLbuZ4gXbB2sX53MKn8JUZ0sB/2wfwsEcRw+I08U=
github.com/minio/md5-simd v1.1.1/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
github.com/minio/minio v0.0.0-20210110213705-828602d672f7/go.mod h1:fp/MFzOSx+MgC/b1epAX6ZLXvBP+zaemt7cV98+UsUg=
github.com/minio/minio v0.0.0-20210128013121-e79829b5b368/go.mod h1:6jySvEwvfCfr9SphRrAb+TMtEXRgRJ4sb79UKFJWmFM=
github.com/minio/minio v0.0.0-20210301081546-0b9c17443eb8/go.mod h1:E7ngQWKJdbRG9dqHZ86lnhGS0RxqtEJTnWDEM/P9BQs=
github.com/minio/minio v0.0.0-20210301203133-e8d8dfa3ae8f h1:dAFaii7oqV0Mu9rUNfsgKdE10aZADSgIXoK5saLVeOE=
github.com/minio/minio v0.0.0-20210301203133-e8d8dfa3ae8f/go.mod h1:E7ngQWKJdbRG9dqHZ86lnhGS0RxqtEJTnWDEM/P9BQs=
github.com/minio/minio v0.0.0-20210421185923-38a9f87a560e/go.mod h1:XNPkjbDN/j7cNOoaE0TefFKeDycjJVkYTGRJlD6I9EI=
github.com/minio/minio v0.0.0-20210423185853-cbfdf97abf9f h1:2IVnOLvfO2HY9n82nHKhLcxVbCPFsVvSCzO33rAgd7w=
github.com/minio/minio v0.0.0-20210423185853-cbfdf97abf9f/go.mod h1:RDrFSTbgdNK7uLIZZqfx93L973cEj6EecG2OGfOeUp8=
github.com/minio/minio-go/v7 v7.0.7-0.20201217170524-3baf9ea06f7c/go.mod h1:pEZBUa+L2m9oECoIA6IcSK8bv/qggtQVLovjeKK5jYc=
github.com/minio/minio-go/v7 v7.0.8-0.20210127003153-c40722862654/go.mod h1:pEZBUa+L2m9oECoIA6IcSK8bv/qggtQVLovjeKK5jYc=
github.com/minio/minio-go/v7 v7.0.10 h1:1oUKe4EOPUEhw2qnPQaPsJ0lmVTYLFu03SiItauXs94=
github.com/minio/minio-go/v7 v7.0.10/go.mod h1:td4gW1ldOsj1PbSNS+WYK43j+P1XVhX/8W8awaYlBFo=
github.com/minio/operator v0.0.0-20210317030027-207337abe7fd h1:ibM7ebC9WdtgJh6p3mqbU0W1vpoFXqJgTHDBKJWGRQI=
github.com/minio/operator v0.0.0-20210317030027-207337abe7fd/go.mod h1:1Bpqm6g8f30YeePauZfSet7EWrWeyqu1eJhY5/4sn28=
github.com/minio/minio-go/v7 v7.0.11-0.20210302210017-6ae69c73ce78/go.mod h1:mTh2uJuAbEqdhMVl6CMIIZLUeiMiWtJR4JB8/5g2skw=
github.com/minio/minio-go/v7 v7.0.11-0.20210407221404-ba867dba7ee1 h1:x0jnjGGnb0PaewBIH9dbTaLJUNVKYoQOjjdqwz9PwdQ=
github.com/minio/minio-go/v7 v7.0.11-0.20210407221404-ba867dba7ee1/go.mod h1:mTh2uJuAbEqdhMVl6CMIIZLUeiMiWtJR4JB8/5g2skw=
github.com/minio/operator v0.0.0-20210419212754-93a9239fd18b h1:3TV7FQpciS08+66ydlSPHSU96hMUjt1696xW6NrAcmk=
github.com/minio/operator v0.0.0-20210419212754-93a9239fd18b/go.mod h1:kPcdt1W5H4gBMzrH1Pz1LHkCD5QQDDwkU62MyYg3O8M=
github.com/minio/operator/logsearchapi v0.0.0-20210201110528-753019b838b4 h1:7HNd0WPMFcQzQbgPs7VQJfiXVm8xjuxnS3/1yi4twwM=
github.com/minio/operator/logsearchapi v0.0.0-20210201110528-753019b838b4/go.mod h1:ngzK3RurLvshJ4XmJ6eP4WTOIc9Vu1HQNq0Hm6XOZmw=
github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=
@@ -929,6 +944,7 @@ github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy
github.com/minio/simdjson-go v0.1.5/go.mod h1:oKURrZZEBtqObgJrSjN1Ln2n9MJj2icuBTkeJzZnvSI=
github.com/minio/simdjson-go v0.2.0/go.mod h1:JPUSkRykfSPS+AhO0YPA1h0l5vY7NqrF4zel2b12wxc=
github.com/minio/simdjson-go v0.2.1/go.mod h1:JPUSkRykfSPS+AhO0YPA1h0l5vY7NqrF4zel2b12wxc=
github.com/minio/sio v0.2.1 h1:NjzKiIMSMcHediVQR0AFVx2tp7Wxh9tKPfDI3kH7aHQ=
github.com/minio/sio v0.2.1/go.mod h1:8b0yPp2avGThviy/+OCJBI6OMpvxoUuiLvE6F1lebhw=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
@@ -976,8 +992,9 @@ github.com/nats-io/jwt v1.1.0/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats-server/v2 v2.1.9 h1:Sxr2zpaapgpBT9ElTxTVe62W+qjnhPcKY/8W5cnA/Qk=
github.com/nats-io/nats-server/v2 v2.1.9/go.mod h1:9qVyoewoYXzG1ME9ox0HwkkzyYvnlBDugfR4Gg/8uHU=
github.com/nats-io/nats-streaming-server v0.19.0 h1:NVYusu6kcMxRBj1wOWRdXBUHf1bzkJQbsHovsg+Fr1o=
github.com/nats-io/nats-streaming-server v0.19.0/go.mod h1:oqrRqpMg84aiPDyroTornjVWNYJKh+6ozh2Mgt8dslE=
github.com/nats-io/nats-streaming-server v0.21.1 h1:jb/osnXmFJtKDS9DFghDjX82v1NT9IhaoR/r6s6toNg=
github.com/nats-io/nats-streaming-server v0.21.1/go.mod h1:2W8QfNVOtcFpmf0bRiwuLtRb0/hkX4NuOxPOFNOThVQ=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nats.go v1.10.0 h1:L8qnKaofSfNFbXg0C5F71LdjPRnmQwSsA4ukmkt1TvY=
github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE=
@@ -988,8 +1005,9 @@ github.com/nats-io/nkeys v0.2.0 h1:WXKF7diOaPU9cJdLD7nuzwasQy9vT1tBqzXZZf3AMJM=
github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nats-io/stan.go v0.7.0 h1:sMVHD9RkxPOl6PJfDVBQd+gbxWkApeYl6GrH+10msO4=
github.com/nats-io/stan.go v0.7.0/go.mod h1:Ci6mUIpGQTjl++MqK2XzkWI/0vF+Bl72uScx7ejSYmU=
github.com/nats-io/stan.go v0.8.3 h1:XyemjL9vAeGHooHn5RQy+ngljd8AVSM2l65Jdnpv4rI=
github.com/nats-io/stan.go v0.8.3/go.mod h1:Ejm8bbHnMTSptU6uNMAVuxeapMJYBB/Ml3ej6z4GoSY=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/ncw/directio v1.0.5 h1:JSUBhdjEvVaJvOoyPAbcW0fnd0tvRXD76wEfZ1KcQz4=
github.com/ncw/directio v1.0.5/go.mod h1:rX/pKEYkOXBGOggmcyJeJGloCkleSvphPx2eV3t6ROk=
@@ -1050,6 +1068,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.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=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
@@ -1074,6 +1093,9 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.44.1/go.mod h1:3WYi4xqXxGGXWDdQIITnLNmuDzO5n6wYva9spVhR4fg=
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.46.0/go.mod h1:3WYi4xqXxGGXWDdQIITnLNmuDzO5n6wYva9spVhR4fg=
github.com/prometheus-operator/prometheus-operator/pkg/client v0.46.0/go.mod h1:k4BrWlVQQsvBiTcDnKEMgyh/euRxyxgrHdur/ZX/sdA=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
@@ -1106,8 +1128,9 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/quasilyte/go-ruleguard v0.1.2-0.20200318202121-b00d7a75d3d8/go.mod h1:CGFX09Ci3pq9QZdj86B+VGIdNj4VyCo2iPOGS9esB/k=
@@ -1166,8 +1189,9 @@ github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.0 h1:nfhvjKcUMhBMVqbKHJlk5RPrrfYr/NMo3692g0dwfWU=
github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
@@ -1230,7 +1254,10 @@ github.com/tetafro/godot v0.3.7/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQx
github.com/tetafro/godot v0.4.2/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
github.com/tidwall/gjson v1.3.5/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
github.com/tidwall/gjson v1.6.7/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI=
github.com/tidwall/gjson v1.6.8 h1:CTmXMClGYPAmln7652e69B7OLXfTi5ABcPPwjIWUv7w=
github.com/tidwall/gjson v1.6.8/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI=
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
@@ -1238,6 +1265,7 @@ github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
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.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=
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
@@ -1267,6 +1295,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/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8=
@@ -1367,8 +1396,10 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 h1:xYJJ3S178yv++9zXV/hnr29plCAGO9vAFG9dorqaFQc=
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc h1:+q90ECDSAQirdykUN6sPEiBXBsp8Csjcca8Oy7bgLTA=
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1401,8 +1432,9 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1451,8 +1483,9 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1471,6 +1504,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1513,6 +1547,7 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1535,6 +1570,7 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/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=
@@ -1542,8 +1578,10 @@ golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 h1:8qxJSnu+7dRq6upnbntrmriWByIakBuct5OM/MdQC1M=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -1572,6 +1610,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=
@@ -1635,6 +1674,7 @@ golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjs
golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200331202046-9d5940d49312/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@@ -1645,13 +1685,16 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/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-20200619180055-7c47624df98f/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-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201105001634-bc3cf281b174/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1-0.20210201201750-4d4ee958a9b7 h1:/wdPW261t381NDQd8TBo63/FyvACfLICwtH8wMRoHJQ=
golang.org/x/tools v0.1.1-0.20210201201750-4d4ee958a9b7/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
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=
@@ -1819,41 +1862,53 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
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=
honnef.co/go/tools v0.0.1-2020.1.6 h1:W18jzjh8mfPez+AwGLxmOImucz/IFjpNlrKVnaj2YVc=
honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY=
k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA=
k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA=
k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI=
k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw=
k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8=
k8s.io/apiextensions-apiserver v0.18.3/go.mod h1:TMsNGs7DYpMXd+8MOCX8KzPOCx8fnZMoIGB24m03+JE=
k8s.io/apiextensions-apiserver v0.20.1/go.mod h1:ntnrZV+6a3dB504qwC5PN/Yg9PBiDNt1EVqbW2kORVk=
k8s.io/apiextensions-apiserver v0.20.2/go.mod h1:F6TXp389Xntt+LUq3vw6HFOLttPa0V8821ogLGwb6Zs=
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/apimachinery v0.17.1-beta.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg=
k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I=
k8s.io/apiserver v0.18.3/go.mod h1:tHQRmthRPLUtwqsOnJJMoI8SW3lnoReZeE861lH8vUw=
k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
k8s.io/apiserver v0.20.2/go.mod h1:2nKd93WyMhZx4Hp3RfgH2K5PhwyTrprrkWYnI7id7jA=
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc=
k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
k8s.io/client-go v0.20.2 h1:uuf+iIAbfnCSw8IGAv/Rg0giM+2bOzHLOsbbrwrdhNQ=
k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE=
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.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
k8s.io/code-generator v0.20.1/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
k8s.io/code-generator v0.20.2/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
k8s.io/code-generator v0.20.4/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
k8s.io/component-base v0.17.4/go.mod h1:5BRqHMbbQPm2kKu35v3G+CpVq4K0RJKC7TRioF0I9lE=
k8s.io/component-base v0.18.3/go.mod h1:bp5GzGR0aGkYEfTj+eTY0AN/vXTgkJdQXjNTTVUaa3k=
k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
k8s.io/component-base v0.20.2/go.mod h1:pzFtCiwe/ASD0iV7ySMu8SYVJjCapNM9bjvk7ptpKh0=
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/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
@@ -1865,10 +1920,14 @@ k8s.io/klog/v2 v2.2.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-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c=
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
k8s.io/legacy-cloud-providers v0.17.4/go.mod h1:FikRNoD64ECjkxO36gkDgJeiQWwyZTuBkhu+yxOc1Js=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
@@ -1885,6 +1944,7 @@ 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/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/controller-runtime v0.8.0 h1:s0dYdo7lQgJiAf+alP82PRwbz+oAqL3oSyMQ18XRDOc=
sigs.k8s.io/controller-runtime v0.8.0/go.mod h1:v9Lbj5oX443uR7GXYY46E0EE2o7k2YxQ58GxVNeXSW4=
@@ -1892,6 +1952,9 @@ sigs.k8s.io/controller-tools v0.5.0/go.mod h1:JTsstrMpxs+9BUj6eGuAaEb6SDSPTeVtUy
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=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -15,7 +15,7 @@ spec:
serviceAccountName: console-sa
containers:
- name: console
image: minio/console:v0.6.6
image: minio/console:v0.7.0
imagePullPolicy: "IfNotPresent"
args:
- server

View File

@@ -15,7 +15,7 @@ spec:
serviceAccountName: console-sa
containers:
- name: console
image: minio/console:v0.6.6
image: minio/console:v0.7.0
imagePullPolicy: "IfNotPresent"
env:
- name: CONSOLE_OPERATOR_MODE

View File

@@ -0,0 +1,93 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// AddBucketLifecycle add bucket lifecycle
//
// swagger:model addBucketLifecycle
type AddBucketLifecycle struct {
// Non required, toggle to disable or enable rule
Disable bool `json:"disable,omitempty"`
// Non required, toggle to disable or enable rule
ExpiredObjectDeleteMarker bool `json:"expired_object_delete_marker,omitempty"`
// Required in case of expiry_days or transition fields are not set. it defines an expiry date for ILM
ExpiryDate string `json:"expiry_date,omitempty"`
// Required in case of expiry_date or transition fields are not set. it defines an expiry days for ILM
ExpiryDays int32 `json:"expiry_days,omitempty"`
// Non required, can be set in case of expiration is enabled
NoncurrentversionExpirationDays int32 `json:"noncurrentversion_expiration_days,omitempty"`
// Non required, can be set in case of transition is enabled
NoncurrentversionTransitionDays int32 `json:"noncurrentversion_transition_days,omitempty"`
// Non required, can be set in case of transition is enabled
NoncurrentversionTransitionStorageClass string `json:"noncurrentversion_transition_storage_class,omitempty"`
// Non required field, it matches a prefix to perform ILM operations on it
Prefix string `json:"prefix,omitempty"`
// Required only in case of transition is set. it refers to a tier
StorageClass string `json:"storage_class,omitempty"`
// Non required field, tags to match ILM files
Tags string `json:"tags,omitempty"`
// Required in case of transition_days or expiry fields are not set. it defines a transition date for ILM
TransitionDate string `json:"transition_date,omitempty"`
// Required in case of transition_date or expiry fields are not set. it defines a transition days for ILM
TransitionDays int32 `json:"transition_days,omitempty"`
}
// Validate validates this add bucket lifecycle
func (m *AddBucketLifecycle) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *AddBucketLifecycle) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *AddBucketLifecycle) UnmarshalBinary(b []byte) error {
var res AddBucketLifecycle
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,97 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// BucketLifecycleResponse bucket lifecycle response
//
// swagger:model bucketLifecycleResponse
type BucketLifecycleResponse struct {
// lifecycle
Lifecycle []*ObjectBucketLifecycle `json:"lifecycle"`
}
// Validate validates this bucket lifecycle response
func (m *BucketLifecycleResponse) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateLifecycle(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *BucketLifecycleResponse) validateLifecycle(formats strfmt.Registry) error {
if swag.IsZero(m.Lifecycle) { // not required
return nil
}
for i := 0; i < len(m.Lifecycle); i++ {
if swag.IsZero(m.Lifecycle[i]) { // not required
continue
}
if m.Lifecycle[i] != nil {
if err := m.Lifecycle[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("lifecycle" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *BucketLifecycleResponse) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *BucketLifecycleResponse) UnmarshalBinary(b []byte) error {
var res BucketLifecycleResponse
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -84,6 +84,9 @@ type CreateTenantRequest struct {
// labels
Labels map[string]string `json:"labels,omitempty"`
// log search configuration
LogSearchConfiguration *LogSearchConfiguration `json:"logSearchConfiguration,omitempty"`
// mounth path
MounthPath string `json:"mounth_path,omitempty"`
@@ -100,6 +103,9 @@ type CreateTenantRequest struct {
// Required: true
Pools []*Pool `json:"pools"`
// prometheus configuration
PrometheusConfiguration *PrometheusConfiguration `json:"prometheusConfiguration,omitempty"`
// secret key
SecretKey string `json:"secret_key,omitempty"`
@@ -127,6 +133,10 @@ func (m *CreateTenantRequest) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validateLogSearchConfiguration(formats); err != nil {
res = append(res, err)
}
if err := m.validateName(formats); err != nil {
res = append(res, err)
}
@@ -139,6 +149,10 @@ func (m *CreateTenantRequest) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validatePrometheusConfiguration(formats); err != nil {
res = append(res, err)
}
if err := m.validateTLS(formats); err != nil {
res = append(res, err)
}
@@ -221,6 +235,24 @@ func (m *CreateTenantRequest) validateImageRegistry(formats strfmt.Registry) err
return nil
}
func (m *CreateTenantRequest) validateLogSearchConfiguration(formats strfmt.Registry) error {
if swag.IsZero(m.LogSearchConfiguration) { // not required
return nil
}
if m.LogSearchConfiguration != nil {
if err := m.LogSearchConfiguration.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("logSearchConfiguration")
}
return err
}
}
return nil
}
func (m *CreateTenantRequest) validateName(formats strfmt.Registry) error {
if err := validate.Required("name", "body", m.Name); err != nil {
@@ -268,6 +300,24 @@ func (m *CreateTenantRequest) validatePools(formats strfmt.Registry) error {
return nil
}
func (m *CreateTenantRequest) validatePrometheusConfiguration(formats strfmt.Registry) error {
if swag.IsZero(m.PrometheusConfiguration) { // not required
return nil
}
if m.PrometheusConfiguration != nil {
if err := m.PrometheusConfiguration.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("prometheusConfiguration")
}
return err
}
}
return nil
}
func (m *CreateTenantRequest) validateTLS(formats strfmt.Registry) error {
if swag.IsZero(m.TLS) { // not required

View File

@@ -0,0 +1,66 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// CsiFormatErrorResponse csi format error response
//
// swagger:model csiFormatErrorResponse
type CsiFormatErrorResponse struct {
// drive
Drive string `json:"drive,omitempty"`
// error
Error string `json:"error,omitempty"`
// node
Node string `json:"node,omitempty"`
}
// Validate validates this csi format error response
func (m *CsiFormatErrorResponse) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *CsiFormatErrorResponse) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *CsiFormatErrorResponse) UnmarshalBinary(b []byte) error {
var res CsiFormatErrorResponse
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,66 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// ExpirationResponse expiration response
//
// swagger:model expirationResponse
type ExpirationResponse struct {
// date
Date string `json:"date,omitempty"`
// days
Days int64 `json:"days,omitempty"`
// delete marker
DeleteMarker bool `json:"delete_marker,omitempty"`
}
// Validate validates this expiration response
func (m *ExpirationResponse) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *ExpirationResponse) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *ExpirationResponse) UnmarshalBinary(b []byte) error {
var res ExpirationResponse
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,99 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// FormatConfiguration format configuration
//
// swagger:model formatConfiguration
type FormatConfiguration struct {
// drives
// Required: true
// Min Length: 1
Drives []string `json:"drives"`
// force
// Required: true
Force *bool `json:"force"`
}
// Validate validates this format configuration
func (m *FormatConfiguration) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateDrives(formats); err != nil {
res = append(res, err)
}
if err := m.validateForce(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *FormatConfiguration) validateDrives(formats strfmt.Registry) error {
if err := validate.Required("drives", "body", m.Drives); err != nil {
return err
}
return nil
}
func (m *FormatConfiguration) validateForce(formats strfmt.Registry) error {
if err := validate.Required("force", "body", m.Force); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *FormatConfiguration) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *FormatConfiguration) UnmarshalBinary(b []byte) error {
var res FormatConfiguration
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,97 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// FormatDirectCSIDrivesResponse format direct c s i drives response
//
// swagger:model formatDirectCSIDrivesResponse
type FormatDirectCSIDrivesResponse struct {
// format issues list
FormatIssuesList []*CsiFormatErrorResponse `json:"formatIssuesList"`
}
// Validate validates this format direct c s i drives response
func (m *FormatDirectCSIDrivesResponse) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateFormatIssuesList(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *FormatDirectCSIDrivesResponse) validateFormatIssuesList(formats strfmt.Registry) error {
if swag.IsZero(m.FormatIssuesList) { // not required
return nil
}
for i := 0; i < len(m.FormatIssuesList); i++ {
if swag.IsZero(m.FormatIssuesList[i]) { // not required
continue
}
if m.FormatIssuesList[i] != nil {
if err := m.FormatIssuesList[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("formatIssuesList" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *FormatDirectCSIDrivesResponse) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *FormatDirectCSIDrivesResponse) UnmarshalBinary(b []byte) error {
var res FormatDirectCSIDrivesResponse
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -23,6 +23,8 @@ 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"
@@ -37,6 +39,9 @@ type IdpConfiguration struct {
// active directory
ActiveDirectory *IdpConfigurationActiveDirectory `json:"active_directory,omitempty"`
// keys
Keys []*IdpConfigurationKeysItems0 `json:"keys"`
// oidc
Oidc *IdpConfigurationOidc `json:"oidc,omitempty"`
}
@@ -49,6 +54,10 @@ func (m *IdpConfiguration) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validateKeys(formats); err != nil {
res = append(res, err)
}
if err := m.validateOidc(formats); err != nil {
res = append(res, err)
}
@@ -77,6 +86,31 @@ func (m *IdpConfiguration) validateActiveDirectory(formats strfmt.Registry) erro
return nil
}
func (m *IdpConfiguration) validateKeys(formats strfmt.Registry) error {
if swag.IsZero(m.Keys) { // not required
return nil
}
for i := 0; i < len(m.Keys); i++ {
if swag.IsZero(m.Keys[i]) { // not required
continue
}
if m.Keys[i] != nil {
if err := m.Keys[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("keys" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *IdpConfiguration) validateOidc(formats strfmt.Registry) error {
if swag.IsZero(m.Oidc) { // not required
@@ -213,6 +247,74 @@ func (m *IdpConfigurationActiveDirectory) UnmarshalBinary(b []byte) error {
return nil
}
// IdpConfigurationKeysItems0 idp configuration keys items0
//
// swagger:model IdpConfigurationKeysItems0
type IdpConfigurationKeysItems0 struct {
// access key
// Required: true
AccessKey *string `json:"access_key"`
// secret key
// Required: true
SecretKey *string `json:"secret_key"`
}
// Validate validates this idp configuration keys items0
func (m *IdpConfigurationKeysItems0) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAccessKey(formats); err != nil {
res = append(res, err)
}
if err := m.validateSecretKey(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IdpConfigurationKeysItems0) validateAccessKey(formats strfmt.Registry) error {
if err := validate.Required("access_key", "body", m.AccessKey); err != nil {
return err
}
return nil
}
func (m *IdpConfigurationKeysItems0) validateSecretKey(formats strfmt.Registry) error {
if err := validate.Required("secret_key", "body", m.SecretKey); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *IdpConfigurationKeysItems0) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IdpConfigurationKeysItems0) UnmarshalBinary(b []byte) error {
var res IdpConfigurationKeysItems0
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// IdpConfigurationOidc idp configuration oidc
//
// swagger:model IdpConfigurationOidc

63
models/lifecycle_tag.go Normal file
View File

@@ -0,0 +1,63 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// LifecycleTag lifecycle tag
//
// swagger:model lifecycleTag
type LifecycleTag struct {
// key
Key string `json:"key,omitempty"`
// value
Value string `json:"value,omitempty"`
}
// Validate validates this lifecycle tag
func (m *LifecycleTag) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *LifecycleTag) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *LifecycleTag) UnmarshalBinary(b []byte) error {
var res LifecycleTag
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,145 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// ListExternalBucketsParams list external buckets params
//
// swagger:model listExternalBucketsParams
type ListExternalBucketsParams struct {
// access key
// Required: true
// Min Length: 3
AccessKey *string `json:"accessKey"`
// region
Region string `json:"region,omitempty"`
// secret key
// Required: true
// Min Length: 8
SecretKey *string `json:"secretKey"`
// target URL
// Required: true
TargetURL *string `json:"targetURL"`
// use TLS
// Required: true
UseTLS *bool `json:"useTLS"`
}
// Validate validates this list external buckets params
func (m *ListExternalBucketsParams) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAccessKey(formats); err != nil {
res = append(res, err)
}
if err := m.validateSecretKey(formats); err != nil {
res = append(res, err)
}
if err := m.validateTargetURL(formats); err != nil {
res = append(res, err)
}
if err := m.validateUseTLS(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *ListExternalBucketsParams) validateAccessKey(formats strfmt.Registry) error {
if err := validate.Required("accessKey", "body", m.AccessKey); err != nil {
return err
}
if err := validate.MinLength("accessKey", "body", string(*m.AccessKey), 3); err != nil {
return err
}
return nil
}
func (m *ListExternalBucketsParams) validateSecretKey(formats strfmt.Registry) error {
if err := validate.Required("secretKey", "body", m.SecretKey); err != nil {
return err
}
if err := validate.MinLength("secretKey", "body", string(*m.SecretKey), 8); err != nil {
return err
}
return nil
}
func (m *ListExternalBucketsParams) validateTargetURL(formats strfmt.Registry) error {
if err := validate.Required("targetURL", "body", m.TargetURL); err != nil {
return err
}
return nil
}
func (m *ListExternalBucketsParams) validateUseTLS(formats strfmt.Registry) error {
if err := validate.Required("useTLS", "body", m.UseTLS); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *ListExternalBucketsParams) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *ListExternalBucketsParams) UnmarshalBinary(b []byte) error {
var res ListExternalBucketsParams
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,69 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// LogSearchConfiguration log search configuration
//
// swagger:model logSearchConfiguration
type LogSearchConfiguration struct {
// image
Image string `json:"image,omitempty"`
// postgres image
PostgresImage string `json:"postgres_image,omitempty"`
// storage class
StorageClass string `json:"storageClass,omitempty"`
// storage size
StorageSize *float64 `json:"storageSize,omitempty"`
}
// Validate validates this log search configuration
func (m *LogSearchConfiguration) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *LogSearchConfiguration) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *LogSearchConfiguration) UnmarshalBinary(b []byte) error {
var res LogSearchConfiguration
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,164 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
"github.com/go-openapi/validate"
)
// MultiBucketReplication multi bucket replication
//
// swagger:model multiBucketReplication
type MultiBucketReplication struct {
// access key
// Required: true
// Min Length: 3
AccessKey *string `json:"accessKey"`
// buckets relation
// Required: true
// Min Length: 1
BucketsRelation []*MultiBucketsRelation `json:"bucketsRelation"`
// region
Region string `json:"region,omitempty"`
// secret key
// Required: true
// Min Length: 8
SecretKey *string `json:"secretKey"`
// target URL
// Required: true
TargetURL *string `json:"targetURL"`
}
// Validate validates this multi bucket replication
func (m *MultiBucketReplication) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAccessKey(formats); err != nil {
res = append(res, err)
}
if err := m.validateBucketsRelation(formats); err != nil {
res = append(res, err)
}
if err := m.validateSecretKey(formats); err != nil {
res = append(res, err)
}
if err := m.validateTargetURL(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *MultiBucketReplication) validateAccessKey(formats strfmt.Registry) error {
if err := validate.Required("accessKey", "body", m.AccessKey); err != nil {
return err
}
if err := validate.MinLength("accessKey", "body", string(*m.AccessKey), 3); err != nil {
return err
}
return nil
}
func (m *MultiBucketReplication) validateBucketsRelation(formats strfmt.Registry) error {
if err := validate.Required("bucketsRelation", "body", m.BucketsRelation); err != nil {
return err
}
for i := 0; i < len(m.BucketsRelation); i++ {
if swag.IsZero(m.BucketsRelation[i]) { // not required
continue
}
if m.BucketsRelation[i] != nil {
if err := m.BucketsRelation[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("bucketsRelation" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *MultiBucketReplication) validateSecretKey(formats strfmt.Registry) error {
if err := validate.Required("secretKey", "body", m.SecretKey); err != nil {
return err
}
if err := validate.MinLength("secretKey", "body", string(*m.SecretKey), 8); err != nil {
return err
}
return nil
}
func (m *MultiBucketReplication) validateTargetURL(formats strfmt.Registry) error {
if err := validate.Required("targetURL", "body", m.TargetURL); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *MultiBucketReplication) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *MultiBucketReplication) UnmarshalBinary(b []byte) error {
var res MultiBucketReplication
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,66 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// MultiBucketResponseItem multi bucket response item
//
// swagger:model multiBucketResponseItem
type MultiBucketResponseItem struct {
// error string
ErrorString string `json:"errorString,omitempty"`
// origin bucket
OriginBucket string `json:"originBucket,omitempty"`
// target bucket
TargetBucket string `json:"targetBucket,omitempty"`
}
// Validate validates this multi bucket response item
func (m *MultiBucketResponseItem) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *MultiBucketResponseItem) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *MultiBucketResponseItem) UnmarshalBinary(b []byte) error {
var res MultiBucketResponseItem
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,97 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// MultiBucketResponseState multi bucket response state
//
// swagger:model multiBucketResponseState
type MultiBucketResponseState struct {
// replication state
ReplicationState []*MultiBucketResponseItem `json:"replicationState"`
}
// Validate validates this multi bucket response state
func (m *MultiBucketResponseState) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateReplicationState(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *MultiBucketResponseState) validateReplicationState(formats strfmt.Registry) error {
if swag.IsZero(m.ReplicationState) { // not required
return nil
}
for i := 0; i < len(m.ReplicationState); i++ {
if swag.IsZero(m.ReplicationState[i]) { // not required
continue
}
if m.ReplicationState[i] != nil {
if err := m.ReplicationState[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("replicationState" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *MultiBucketResponseState) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *MultiBucketResponseState) UnmarshalBinary(b []byte) error {
var res MultiBucketResponseState
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,63 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// MultiBucketsRelation multi buckets relation
//
// swagger:model multiBucketsRelation
type MultiBucketsRelation struct {
// destination bucket
DestinationBucket string `json:"destinationBucket,omitempty"`
// origin bucket
OriginBucket string `json:"originBucket,omitempty"`
}
// Validate validates this multi buckets relation
func (m *MultiBucketsRelation) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *MultiBucketsRelation) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *MultiBucketsRelation) UnmarshalBinary(b []byte) error {
var res MultiBucketsRelation
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,156 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// ObjectBucketLifecycle object bucket lifecycle
//
// swagger:model objectBucketLifecycle
type ObjectBucketLifecycle struct {
// expiration
Expiration *ExpirationResponse `json:"expiration,omitempty"`
// id
ID string `json:"id,omitempty"`
// prefix
Prefix string `json:"prefix,omitempty"`
// status
Status string `json:"status,omitempty"`
// tags
Tags []*LifecycleTag `json:"tags"`
// transition
Transition *TransitionResponse `json:"transition,omitempty"`
}
// Validate validates this object bucket lifecycle
func (m *ObjectBucketLifecycle) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateExpiration(formats); err != nil {
res = append(res, err)
}
if err := m.validateTags(formats); err != nil {
res = append(res, err)
}
if err := m.validateTransition(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *ObjectBucketLifecycle) validateExpiration(formats strfmt.Registry) error {
if swag.IsZero(m.Expiration) { // not required
return nil
}
if m.Expiration != nil {
if err := m.Expiration.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("expiration")
}
return err
}
}
return nil
}
func (m *ObjectBucketLifecycle) validateTags(formats strfmt.Registry) error {
if swag.IsZero(m.Tags) { // not required
return nil
}
for i := 0; i < len(m.Tags); i++ {
if swag.IsZero(m.Tags[i]) { // not required
continue
}
if m.Tags[i] != nil {
if err := m.Tags[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("tags" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *ObjectBucketLifecycle) validateTransition(formats strfmt.Registry) error {
if swag.IsZero(m.Transition) { // not required
return nil
}
if m.Transition != nil {
if err := m.Transition.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("transition")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *ObjectBucketLifecycle) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *ObjectBucketLifecycle) UnmarshalBinary(b []byte) error {
var res ObjectBucketLifecycle
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,66 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// PrometheusConfiguration prometheus configuration
//
// swagger:model prometheusConfiguration
type PrometheusConfiguration struct {
// image
Image string `json:"image,omitempty"`
// storage class
StorageClass string `json:"storageClass,omitempty"`
// storage size
StorageSize *float64 `json:"storageSize,omitempty"`
}
// Validate validates this prometheus configuration
func (m *PrometheusConfiguration) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *PrometheusConfiguration) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *PrometheusConfiguration) UnmarshalBinary(b []byte) error {
var res PrometheusConfiguration
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -36,6 +36,9 @@ import (
// swagger:model sessionResponse
type SessionResponse struct {
// features
Features []string `json:"features"`
// operator
Operator bool `json:"operator,omitempty"`

198
models/tier.go Normal file
View File

@@ -0,0 +1,198 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Tier tier
//
// swagger:model tier
type Tier struct {
// azure
Azure *TierAzure `json:"azure,omitempty"`
// gcs
Gcs *TierGcs `json:"gcs,omitempty"`
// s3
S3 *TierS3 `json:"s3,omitempty"`
// type
// Enum: [s3 gcs azure unsupported]
Type string `json:"type,omitempty"`
}
// Validate validates this tier
func (m *Tier) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateAzure(formats); err != nil {
res = append(res, err)
}
if err := m.validateGcs(formats); err != nil {
res = append(res, err)
}
if err := m.validateS3(formats); err != nil {
res = append(res, err)
}
if err := m.validateType(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Tier) validateAzure(formats strfmt.Registry) error {
if swag.IsZero(m.Azure) { // not required
return nil
}
if m.Azure != nil {
if err := m.Azure.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("azure")
}
return err
}
}
return nil
}
func (m *Tier) validateGcs(formats strfmt.Registry) error {
if swag.IsZero(m.Gcs) { // not required
return nil
}
if m.Gcs != nil {
if err := m.Gcs.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("gcs")
}
return err
}
}
return nil
}
func (m *Tier) validateS3(formats strfmt.Registry) error {
if swag.IsZero(m.S3) { // not required
return nil
}
if m.S3 != nil {
if err := m.S3.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("s3")
}
return err
}
}
return nil
}
var tierTypeTypePropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["s3","gcs","azure","unsupported"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
tierTypeTypePropEnum = append(tierTypeTypePropEnum, v)
}
}
const (
// TierTypeS3 captures enum value "s3"
TierTypeS3 string = "s3"
// TierTypeGcs captures enum value "gcs"
TierTypeGcs string = "gcs"
// TierTypeAzure captures enum value "azure"
TierTypeAzure string = "azure"
// TierTypeUnsupported captures enum value "unsupported"
TierTypeUnsupported string = "unsupported"
)
// prop value enum
func (m *Tier) validateTypeEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, tierTypeTypePropEnum, true); err != nil {
return err
}
return nil
}
func (m *Tier) validateType(formats strfmt.Registry) error {
if swag.IsZero(m.Type) { // not required
return nil
}
// value enum
if err := m.validateTypeEnum("type", "body", m.Type); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *Tier) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Tier) UnmarshalBinary(b []byte) error {
var res Tier
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

78
models/tier_azure.go Normal file
View File

@@ -0,0 +1,78 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// TierAzure tier azure
//
// swagger:model tier_azure
type TierAzure struct {
// accountkey
Accountkey string `json:"accountkey,omitempty"`
// accountname
Accountname string `json:"accountname,omitempty"`
// bucket
Bucket string `json:"bucket,omitempty"`
// endpoint
Endpoint string `json:"endpoint,omitempty"`
// name
Name string `json:"name,omitempty"`
// prefix
Prefix string `json:"prefix,omitempty"`
// region
Region string `json:"region,omitempty"`
}
// Validate validates this tier azure
func (m *TierAzure) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *TierAzure) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *TierAzure) UnmarshalBinary(b []byte) error {
var res TierAzure
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,66 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// TierCredentialsRequest tier credentials request
//
// swagger:model tierCredentialsRequest
type TierCredentialsRequest struct {
// access key
AccessKey string `json:"access_key,omitempty"`
// a base64 encoded value
Creds string `json:"creds,omitempty"`
// secret key
SecretKey string `json:"secret_key,omitempty"`
}
// Validate validates this tier credentials request
func (m *TierCredentialsRequest) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *TierCredentialsRequest) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *TierCredentialsRequest) UnmarshalBinary(b []byte) error {
var res TierCredentialsRequest
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

75
models/tier_gcs.go Normal file
View File

@@ -0,0 +1,75 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// TierGcs tier gcs
//
// swagger:model tier_gcs
type TierGcs struct {
// bucket
Bucket string `json:"bucket,omitempty"`
// creds
Creds string `json:"creds,omitempty"`
// endpoint
Endpoint string `json:"endpoint,omitempty"`
// name
Name string `json:"name,omitempty"`
// prefix
Prefix string `json:"prefix,omitempty"`
// region
Region string `json:"region,omitempty"`
}
// Validate validates this tier gcs
func (m *TierGcs) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *TierGcs) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *TierGcs) UnmarshalBinary(b []byte) error {
var res TierGcs
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,97 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// TierListResponse tier list response
//
// swagger:model tierListResponse
type TierListResponse struct {
// items
Items []*Tier `json:"items"`
}
// Validate validates this tier list response
func (m *TierListResponse) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateItems(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *TierListResponse) validateItems(formats strfmt.Registry) error {
if swag.IsZero(m.Items) { // not required
return nil
}
for i := 0; i < len(m.Items); i++ {
if swag.IsZero(m.Items[i]) { // not required
continue
}
if m.Items[i] != nil {
if err := m.Items[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("items" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *TierListResponse) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *TierListResponse) UnmarshalBinary(b []byte) error {
var res TierListResponse
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

81
models/tier_s3.go Normal file
View File

@@ -0,0 +1,81 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// TierS3 tier s3
//
// swagger:model tier_s3
type TierS3 struct {
// accesskey
Accesskey string `json:"accesskey,omitempty"`
// bucket
Bucket string `json:"bucket,omitempty"`
// endpoint
Endpoint string `json:"endpoint,omitempty"`
// name
Name string `json:"name,omitempty"`
// prefix
Prefix string `json:"prefix,omitempty"`
// region
Region string `json:"region,omitempty"`
// secretkey
Secretkey string `json:"secretkey,omitempty"`
// storageclass
Storageclass string `json:"storageclass,omitempty"`
}
// Validate validates this tier s3
func (m *TierS3) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *TierS3) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *TierS3) UnmarshalBinary(b []byte) error {
var res TierS3
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,66 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// TransitionResponse transition response
//
// swagger:model transitionResponse
type TransitionResponse struct {
// date
Date string `json:"date,omitempty"`
// days
Days int64 `json:"days,omitempty"`
// storage class
StorageClass string `json:"storage_class,omitempty"`
}
// Validate validates this transition response
func (m *TransitionResponse) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *TransitionResponse) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *TransitionResponse) UnmarshalBinary(b []byte) error {
var res TransitionResponse
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,63 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package 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"
)
// UpdateBucketLifecycle update bucket lifecycle
//
// swagger:model updateBucketLifecycle
type UpdateBucketLifecycle struct {
// disable
Disable bool `json:"disable,omitempty"`
// tags
Tags string `json:"tags,omitempty"`
}
// Validate validates this update bucket lifecycle
func (m *UpdateBucketLifecycle) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *UpdateBucketLifecycle) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *UpdateBucketLifecycle) UnmarshalBinary(b []byte) error {
var res UpdateBucketLifecycle
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

10
node_modules/.yarn-integrity generated vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"systemParams": "darwin-x64-88",
"modulesFolders": [],
"flags": [],
"linkedModules": [],
"topLevelPatterns": [],
"lockfileEntries": {},
"files": [],
"artifacts": {}
}

View File

@@ -177,7 +177,7 @@ func TestGetActionsStringFromPolicy(t *testing.T) {
args: args{
policy: &iampolicy.AdminDiagnostics,
},
want: 7,
want: 8,
},
}
for _, tt := range tests {

View File

@@ -66,3 +66,13 @@ var defaultSaltForIdpHmac = utils.RandomCharString(64)
func getSaltForIdpHmac() string {
return env.Get(ConsoleIdpHmacSalt, defaultSaltForIdpHmac)
}
// getIdpScopes return default scopes during the IDP login request
func getIdpScopes() string {
return env.Get(ConsoleIDPScopes, "openid,profile,email")
}
// getIdpTokenExpiration return default token expiration for access token (in seconds)
func getIdpTokenExpiration() string {
return env.Get(ConsoleIDPTokenExpiration, "3600")
}

View File

@@ -18,11 +18,13 @@ package oauth2
const (
// const for idp configuration
ConsoleMinIOServer = "CONSOLE_MINIO_SERVER"
ConsoleIdpURL = "CONSOLE_IDP_URL"
ConsoleIdpClientID = "CONSOLE_IDP_CLIENT_ID"
ConsoleIdpSecret = "CONSOLE_IDP_SECRET"
ConsoleIdpCallbackURL = "CONSOLE_IDP_CALLBACK"
ConsoleIdpHmacPassphrase = "CONSOLE_IDP_HMAC_PASSPHRASE"
ConsoleIdpHmacSalt = "CONSOLE_IDP_HMAC_SALT"
ConsoleMinIOServer = "CONSOLE_MINIO_SERVER"
ConsoleIdpURL = "CONSOLE_IDP_URL"
ConsoleIdpClientID = "CONSOLE_IDP_CLIENT_ID"
ConsoleIdpSecret = "CONSOLE_IDP_SECRET"
ConsoleIdpCallbackURL = "CONSOLE_IDP_CALLBACK"
ConsoleIdpHmacPassphrase = "CONSOLE_IDP_HMAC_PASSPHRASE"
ConsoleIdpHmacSalt = "CONSOLE_IDP_HMAC_SALT"
ConsoleIDPScopes = "CONSOLE_IDP_SCOPES"
ConsoleIDPTokenExpiration = "CONSOLE_IDP_TOKEN_EXPIRATION"
)

View File

@@ -25,6 +25,7 @@ import (
"log"
"net/http"
"net/url"
"strconv"
"strings"
"time"
@@ -110,9 +111,19 @@ func NewOauth2ProviderClient(ctx context.Context, scopes []string, httpClient *h
if err != nil {
return nil, err
}
// If provided scopes are empty we use a default list
// if google, change scopes
u, err := url.Parse(GetIdpURL())
if err != nil {
return nil, err
}
// below verification should not be necessary if the user configure exactly the
// scopes he need, will be removed on a future release
if u.Host == "google.com" {
scopes = []string{oidc.ScopeOpenID}
}
// If provided scopes are empty we use a default list or the user configured list
if len(scopes) == 0 {
scopes = []string{oidc.ScopeOpenID, "profile", "app_metadata", "user_metadata", "email"}
scopes = strings.Split(getIdpScopes(), ",")
}
client := new(Provider)
client.oauth2Config = &xoauth2.Config{
@@ -169,9 +180,22 @@ func (client *Provider) VerifyIdentity(ctx context.Context, code, state string)
return nil, errors.New("invalid token")
}
// expiration configured in the token itself
expiration := int(oauth2Token.Expiry.Sub(time.Now().UTC()).Seconds())
// check if user configured a hardcoded expiration for console via env variables
// and override the incoming expiration
userConfiguredExpiration := getIdpTokenExpiration()
if userConfiguredExpiration != "" {
expiration, _ = strconv.Atoi(userConfiguredExpiration)
}
idToken := oauth2Token.Extra("id_token")
if idToken == nil {
return nil, errors.New("returned token is missing id_token claim")
}
return &credentials.WebIdentityToken{
Token: oauth2Token.Extra("id_token").(string),
Expiry: int(oauth2Token.Expiry.Sub(time.Now().UTC()).Seconds()),
Token: idToken.(string),
Expiry: expiration,
}, nil
}
stsEndpoint := GetSTSEndpoint()

BIN
portal-ui/build/amazon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@@ -1,25 +1,25 @@
{
"files": {
"main.css": "/static/css/main.a19f3d53.chunk.css",
"main.js": "/static/js/main.fa0873e1.chunk.js",
"main.js.map": "/static/js/main.fa0873e1.chunk.js.map",
"main.js": "/static/js/main.ca4ff6a7.chunk.js",
"main.js.map": "/static/js/main.ca4ff6a7.chunk.js.map",
"runtime-main.js": "/static/js/runtime-main.f48e99e5.js",
"runtime-main.js.map": "/static/js/runtime-main.f48e99e5.js.map",
"static/css/2.f324abd6.chunk.css": "/static/css/2.f324abd6.chunk.css",
"static/js/2.44b7c49b.chunk.js": "/static/js/2.44b7c49b.chunk.js",
"static/js/2.44b7c49b.chunk.js.map": "/static/js/2.44b7c49b.chunk.js.map",
"static/css/2.32daf8f7.chunk.css": "/static/css/2.32daf8f7.chunk.css",
"static/js/2.1e7db62a.chunk.js": "/static/js/2.1e7db62a.chunk.js",
"static/js/2.1e7db62a.chunk.js.map": "/static/js/2.1e7db62a.chunk.js.map",
"index.html": "/index.html",
"static/css/2.f324abd6.chunk.css.map": "/static/css/2.f324abd6.chunk.css.map",
"static/css/2.32daf8f7.chunk.css.map": "/static/css/2.32daf8f7.chunk.css.map",
"static/css/main.a19f3d53.chunk.css.map": "/static/css/main.a19f3d53.chunk.css.map",
"static/js/2.44b7c49b.chunk.js.LICENSE.txt": "/static/js/2.44b7c49b.chunk.js.LICENSE.txt",
"static/js/2.1e7db62a.chunk.js.LICENSE.txt": "/static/js/2.1e7db62a.chunk.js.LICENSE.txt",
"static/media/minio_console_logo.0837460e.svg": "/static/media/minio_console_logo.0837460e.svg",
"static/media/minio_operator_logo.1312b7c9.svg": "/static/media/minio_operator_logo.1312b7c9.svg"
},
"entrypoints": [
"static/js/runtime-main.f48e99e5.js",
"static/css/2.f324abd6.chunk.css",
"static/js/2.44b7c49b.chunk.js",
"static/css/2.32daf8f7.chunk.css",
"static/js/2.1e7db62a.chunk.js",
"static/css/main.a19f3d53.chunk.css",
"static/js/main.fa0873e1.chunk.js"
"static/js/main.ca4ff6a7.chunk.js"
]
}

BIN
portal-ui/build/azure.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
portal-ui/build/gcs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="MinIO Console"/><link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700;900&display=swap" rel="stylesheet"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/manifest.json"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3a4e54"/><title>MinIO Console</title><link href="/static/css/2.f324abd6.chunk.css" rel="stylesheet"><link href="/static/css/main.a19f3d53.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,i=r[0],a=r[1],p=r[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var a=t[i];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var i=this["webpackJsonpportal-ui"]=this["webpackJsonpportal-ui"]||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var p=0;p<i.length;p++)r(i[p]);var f=a;t()}([])</script><script src="/static/js/2.44b7c49b.chunk.js"></script><script src="/static/js/main.fa0873e1.chunk.js"></script></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="MinIO Console"/><link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700;900&display=swap" rel="stylesheet"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/manifest.json"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3a4e54"/><title>MinIO Console</title><link href="/static/css/2.32daf8f7.chunk.css" rel="stylesheet"><link href="/static/css/main.a19f3d53.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,i=r[0],a=r[1],p=r[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var a=t[i];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var i=this["webpackJsonpportal-ui"]=this["webpackJsonpportal-ui"]||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var p=0;p<i.length;p++)r(i[p]);var f=a;t()}([])</script><script src="/static/js/2.1e7db62a.chunk.js"></script><script src="/static/js/main.ca4ff6a7.chunk.js"></script></body></html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -1,2 +1,2 @@
.ReactVirtualized__Table__headerRow{font-weight:700;text-transform:uppercase}.ReactVirtualized__Table__headerRow,.ReactVirtualized__Table__row{display:flex;flex-direction:row;align-items:center}.ReactVirtualized__Table__headerTruncatedText{display:inline-block;max-width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ReactVirtualized__Table__headerColumn,.ReactVirtualized__Table__rowColumn{margin-right:10px;min-width:0}.ReactVirtualized__Table__rowColumn{text-overflow:ellipsis;white-space:nowrap}.ReactVirtualized__Table__headerColumn:first-of-type,.ReactVirtualized__Table__rowColumn:first-of-type{margin-left:10px}.ReactVirtualized__Table__sortableHeaderColumn{cursor:pointer}.ReactVirtualized__Table__sortableHeaderIconContainer{display:flex;align-items:center}.ReactVirtualized__Table__sortableHeaderIcon{flex:0 0 24px;height:1em;width:1em;fill:currentColor}.react-grid-layout{position:relative;transition:height .2s ease}.react-grid-item{transition:all .2s ease;transition-property:left,top}.react-grid-item img{pointer-events:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.react-grid-item.cssTransforms{transition-property:transform}.react-grid-item.resizing{z-index:1;will-change:width,height}.react-grid-item.react-draggable-dragging{transition:none;z-index:3;will-change:transform}.react-grid-item.dropping{visibility:hidden}.react-grid-item.react-grid-placeholder{background:red;opacity:.2;transition-duration:.1s;z-index:2;-webkit-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.react-grid-item>.react-resizable-handle{position:absolute;width:20px;height:20px}.react-grid-item>.react-resizable-handle:after{content:"";position:absolute;right:3px;bottom:3px;width:5px;height:5px;border-right:2px solid rgba(0,0,0,.4);border-bottom:2px solid rgba(0,0,0,.4)}.react-resizable-hide>.react-resizable-handle{display:none}.react-grid-item>.react-resizable-handle.react-resizable-handle-sw{bottom:0;left:0;cursor:sw-resize;transform:rotate(90deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-se{bottom:0;right:0;cursor:se-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-nw{top:0;left:0;cursor:nw-resize;transform:rotate(180deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-ne{top:0;right:0;cursor:ne-resize;transform:rotate(270deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-e,.react-grid-item>.react-resizable-handle.react-resizable-handle-w{top:50%;margin-top:-10px;cursor:ew-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-w{left:0;transform:rotate(135deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-e{right:0;transform:rotate(315deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-n,.react-grid-item>.react-resizable-handle.react-resizable-handle-s{left:50%;margin-left:-10px;cursor:ns-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-n{top:0;transform:rotate(225deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-s{bottom:0;transform:rotate(45deg)}.react-resizable{position:relative}.react-resizable-handle{position:absolute;width:20px;height:20px;background-repeat:no-repeat;background-origin:content-box;box-sizing:border-box;background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgd2lkdGg9IjYiIGhlaWdodD0iNiI+PHBhdGggZD0iTTYgNkgwVjQuMmg0LjJWMEg2djZ6IiBvcGFjaXR5PSIuMzAyIi8+PC9zdmc+");background-position:100% 100%;padding:0 3px 3px 0}.react-resizable-handle-sw{bottom:0;left:0;cursor:sw-resize;transform:rotate(90deg)}.react-resizable-handle-se{bottom:0;right:0;cursor:se-resize}.react-resizable-handle-nw{top:0;left:0;cursor:nw-resize;transform:rotate(180deg)}.react-resizable-handle-ne{top:0;right:0;cursor:ne-resize;transform:rotate(270deg)}.react-resizable-handle-e,.react-resizable-handle-w{top:50%;margin-top:-10px;cursor:ew-resize}.react-resizable-handle-w{left:0;transform:rotate(135deg)}.react-resizable-handle-e{right:0;transform:rotate(315deg)}.react-resizable-handle-n,.react-resizable-handle-s{left:50%;margin-left:-10px;cursor:ns-resize}.react-resizable-handle-n{top:0;transform:rotate(225deg)}.react-resizable-handle-s{bottom:0;transform:rotate(45deg)}
/*# sourceMappingURL=2.f324abd6.chunk.css.map */
/*# sourceMappingURL=2.32daf8f7.chunk.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
portal-ui/public/amazon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

BIN
portal-ui/public/azure.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
portal-ui/public/gcs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -14,6 +14,8 @@
// 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 { ILabelKeyPair } from "../screens/Console/Tenants/types";
export interface ITenantsObject {
tenants: ITenant[];
}
@@ -54,6 +56,8 @@ export interface ITenantCreator {
enable_tls: boolean;
access_key: string;
secret_key: string;
access_keys: string[];
secret_keys: string[];
image: string;
console_image: string;
expose_minio: boolean;
@@ -66,6 +70,9 @@ export interface ITenantCreator {
idp?: IIDPConfiguration;
annotations?: Object;
image_registry?: ImageRegistry;
logSearchConfiguration?: LogSearchConfiguration;
prometheusConfiguration?: PrometheusConfiguration;
affinity?: AffinityConfiguration;
}
export interface ImageRegistry {
@@ -91,7 +98,8 @@ export interface ITenantUsage {
}
export interface IAffinityModel {
podAntiAffinity: IPodAntiAffinityModel;
podAntiAffinity?: IPodAntiAffinityModel;
podAffinity?: IPodAffinityModel;
}
export interface IPodAntiAffinityModel {
@@ -107,6 +115,19 @@ export interface IPodAffinityTermLabelSelector {
matchExpressions: IMatchExpressionItem[];
}
export interface IPodAffinityModel {
requiredDuringSchedulingIgnoredDuringExecution: IPodAffinityTerms[];
}
export interface IPodAffinityTerms {
labelSelector: IPodAffinityLabelsSelector;
topologyKey: string;
}
export interface IPodAffinityLabelsSelector {
matchLabels: object;
}
export interface IMatchExpressionItem {
key: string;
operator: string;
@@ -339,3 +360,21 @@ export interface IErasureCodeCalc {
defaultEC: string;
storageFactors: IStorageFactors[];
}
export interface LogSearchConfiguration {
storageClass?: string;
storageSize?: number;
image: string;
postgres_image: string;
}
export interface PrometheusConfiguration {
storageClass?: string;
storageSize?: number;
image: string;
}
export interface AffinityConfiguration {
affinityType: "default" | "nodeSelector" | "none";
nodeSelectorLabels?: ILabelKeyPair[];
}

View File

@@ -25,6 +25,7 @@ import { NewServiceAccount } from "../Common/CredentialsPrompt/types";
import CodeMirrorWrapper from "../Common/FormComponents/CodeMirrorWrapper/CodeMirrorWrapper";
import { setModalErrorSnackMessage } from "../../../actions";
import { connect } from "react-redux";
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
const styles = (theme: Theme) =>
createStyles({
@@ -39,6 +40,11 @@ const styles = (theme: Theme) =>
color: "#393939",
fontSize: 12,
fontStyle: "italic",
marginBottom: "8px",
},
containerScrollable: {
maxHeight: "calc(100vh - 300px)" as const,
overflowY: "auto" as const,
},
...modalBasic,
});
@@ -56,8 +62,11 @@ const AddServiceAccount = ({
closeModalAndRefresh,
setModalErrorSnackMessage,
}: IAddServiceAccountProps) => {
const [addSending, setAddSending] = useState(false);
const [policyDefinition, setPolicyDefinition] = useState("");
const [addSending, setAddSending] = useState<boolean>(false);
const [policyDefinition, setPolicyDefinition] = useState<string>("");
const [isRestrictedByPolicy, setIsRestrictedByPolicy] = useState<boolean>(
false
);
useEffect(() => {
if (addSending) {
@@ -106,8 +115,8 @@ const AddServiceAccount = ({
addServiceAccount(e);
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
<Grid container className={classes.containerScrollable}>
<Grid item xs={12}>
<div className={classes.infoDetails}>
Service Accounts inherit the policy explicitly attached to the
parent user and the policy attached to each group in which the
@@ -117,13 +126,32 @@ const AddServiceAccount = ({
parent user. You cannot modify the Service Account optional policy
after saving.
</div>
<CodeMirrorWrapper
value={policyDefinition}
onBeforeChange={(editor, data, value) => {
setPolicyDefinition(value);
</Grid>
<Grid item xs={12}>
<FormSwitchWrapper
value="locking"
id="locking"
name="locking"
checked={isRestrictedByPolicy}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
setIsRestrictedByPolicy(event.target.checked);
}}
label={"Restrict with policy"}
indicatorLabels={["On", "Off"]}
/>
</Grid>
{isRestrictedByPolicy && (
<Grid item xs={12}>
<CodeMirrorWrapper
value={policyDefinition}
onBeforeChange={(editor, data, value) => {
setPolicyDefinition(value);
}}
/>
</Grid>
)}
</Grid>
<Grid container>
<Grid item xs={12} className={classes.buttonContainer}>
<button
type="button"

View File

@@ -238,12 +238,13 @@ const AddBucket = ({
quotaSize,
quotaUnit,
quotaEnabled,
retentionEnabled,
addBucketRetention,
addBucketRetentionMode,
addBucketRetentionUnit,
addBucketRetentionValidity,
retentionValidity,
versioningEnabled,
enableObjectLocking,
]);
return (

View File

@@ -0,0 +1,458 @@
// 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, { useState, useEffect, Fragment } from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Tooltip } from "@material-ui/core";
import get from "lodash/get";
import Grid from "@material-ui/core/Grid";
import ErrorOutlineIcon from "@material-ui/icons/ErrorOutline";
import CheckCircleOutlineIcon from "@material-ui/icons/CheckCircleOutline";
import {
modalBasic,
wizardCommon,
} from "../../Common/FormComponents/common/styleLibrary";
import { setModalErrorSnackMessage } from "../../../../actions";
import { BulkReplicationResponse, BulkReplicationItem } from "../types";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
import PredefinedList from "../../Common/FormComponents/PredefinedList/PredefinedList";
import api from "../../../../common/api";
import GenericWizard from "../../Common/GenericWizard/GenericWizard";
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
import { SelectorTypes } from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
interface IBulkReplicationModal {
open: boolean;
closeModalAndRefresh: (clearSelection: boolean) => any;
classes: any;
buckets: string[];
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
}
const styles = (theme: Theme) =>
createStyles({
minTableHeader: {
color: "#393939",
"& tr": {
"& th": {
fontWeight: "bold",
},
},
},
buttonContainer: {
textAlign: "right",
},
remoteBucketList: {
display: "grid",
gridTemplateColumns: "auto auto 45px",
alignItems: "center",
justifyContent: "stretch",
},
errorIcon: {
color: "#C72C48",
},
successIcon: {
color: "#42C91A",
},
hide: {
opacity: 0,
transitionDuration: "0.3s",
},
...modalBasic,
...wizardCommon,
});
const AddBulkReplicationModal = ({
open,
closeModalAndRefresh,
classes,
buckets,
setModalErrorSnackMessage,
}: IBulkReplicationModal) => {
const [bucketsToAlter, setBucketsToAlter] = useState<string[]>([]);
const [addLoading, setAddLoading] = useState<boolean>(false);
const [externalLoading, setExternalLoading] = useState<boolean>(false);
const [accessKey, setAccessKey] = useState<string>("");
const [secretKey, setSecretKey] = useState<string>("");
const [targetURL, setTargetURL] = useState<string>("");
const [region, setRegion] = useState<string>("");
const [useTLS, setUseTLS] = useState<boolean>(true);
const [relationBuckets, setRelationBuckets] = useState<string[]>([]);
const [remoteBucketsOpts, setRemoteBucketOpts] = useState<string[]>([]);
const [responseItem, setResponseItem] = useState<BulkReplicationItem[]>([]);
const optionsForBucketsDrop: SelectorTypes[] = remoteBucketsOpts.map(
(remoteBucketName: string) => {
return {
label: remoteBucketName,
value: remoteBucketName,
};
}
);
useEffect(() => {
if (relationBuckets.length === 0) {
const bucketsAlter: string[] = [];
const relationBucketsAlter: string[] = [];
buckets.forEach((item: string) => {
bucketsAlter.push(item);
relationBucketsAlter.push("");
});
setRelationBuckets(relationBucketsAlter);
setBucketsToAlter(bucketsAlter);
}
}, [buckets, relationBuckets.length]);
const addRecord = () => {
setAddLoading(true);
const replicate = bucketsToAlter.map((bucketName, index) => {
return {
originBucket: bucketName,
destinationBucket: relationBuckets[index],
};
});
const endURL = `${useTLS ? "https://" : "http://"}${targetURL}`;
const remoteBucketsInfo = {
accessKey: accessKey,
secretKey: secretKey,
targetURL: endURL,
region: region,
bucketsRelation: replicate,
};
api
.invoke("POST", "/api/v1/buckets-replication", remoteBucketsInfo)
.then((response: BulkReplicationResponse) => {
setAddLoading(false);
const states = response.replicationState;
setResponseItem(states);
const filterErrors = states.filter(
(itm) => itm.errorString && itm.errorString !== ""
);
if (filterErrors.length === 0) {
closeModalAndRefresh(true);
} else {
setTimeout(() => {
removeSuccessItems(states);
}, 500);
}
})
.catch((err) => {
setAddLoading(false);
setModalErrorSnackMessage(err);
});
};
const retrieveRemoteBuckets = (
wizardPageJump: (page: number | string) => void
) => {
const remoteConnectInfo = {
accessKey: accessKey,
secretKey: secretKey,
targetURL: targetURL,
useTLS,
};
setExternalLoading(true);
api
.invoke("POST", "api/v1/list-external-buckets", remoteConnectInfo)
.then((dataReturn) => {
const buckets = get(dataReturn, "buckets", []);
if (buckets && buckets.length > 0) {
const arrayReplaceBuckets = buckets.map((element: any) => {
return element.name;
});
setRemoteBucketOpts(arrayReplaceBuckets);
}
wizardPageJump("++");
setExternalLoading(false);
})
.catch((err) => {
setExternalLoading(false);
setModalErrorSnackMessage(err);
});
};
const stateOfItem = (initialBucket: string) => {
if (responseItem.length > 0) {
const bucketResponse = responseItem.find(
(item) => item.originBucket === initialBucket
);
if (bucketResponse) {
const errString = get(bucketResponse, "errorString", "");
if (errString) {
return errString;
}
return "";
}
}
return "n/a";
};
const LogoToShow = ({ errString }: { errString: string }) => {
switch (errString) {
case "":
return (
<div className={classes.successIcon}>
<CheckCircleOutlineIcon />
</div>
);
case "n/a":
return null;
default:
if (errString) {
return (
<div className={classes.errorIcon}>
<Tooltip title={errString} placement="top-start">
<ErrorOutlineIcon />
</Tooltip>
</div>
);
}
}
return null;
};
const updateItem = (indexItem: number, value: string) => {
const updatedList = [...relationBuckets];
updatedList[indexItem] = value;
setRelationBuckets(updatedList);
};
const itemDisplayBulk = (indexItem: number) => {
if (remoteBucketsOpts.length > 0) {
return (
<Fragment>
<SelectWrapper
label=""
id={`assign-bucket-${indexItem}`}
name={`assign-bucket-${indexItem}`}
value={relationBuckets[indexItem]}
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
updateItem(indexItem, e.target.value as string);
}}
options={optionsForBucketsDrop}
disabled={addLoading}
/>
</Fragment>
);
}
return (
<Fragment>
<InputBoxWrapper
id={`assign-bucket-${indexItem}`}
name={`assign-bucket-${indexItem}`}
label=""
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
updateItem(indexItem, event.target.value);
}}
value={relationBuckets[indexItem]}
disabled={addLoading}
/>
</Fragment>
);
};
const removeSuccessItems = (responseItem: BulkReplicationItem[]) => {
let newBucketsToAlter = [...bucketsToAlter];
let newRelationBuckets = [...relationBuckets];
responseItem.forEach((successElement) => {
const errorString = get(successElement, "errorString", "");
if (!errorString || errorString === "") {
const indexToRemove = newBucketsToAlter.indexOf(
successElement.originBucket
);
newBucketsToAlter.splice(indexToRemove, 1);
newRelationBuckets.splice(indexToRemove, 1);
}
});
setBucketsToAlter(newBucketsToAlter);
setRelationBuckets(newRelationBuckets);
};
return (
<ModalWrapper
modalOpen={open}
onClose={() => {
closeModalAndRefresh(false);
}}
title="Set Multiple Bucket Replication"
>
<GenericWizard
loadingStep={addLoading || externalLoading}
wizardSteps={[
{
label: "Remote Configuration",
componentRender: (
<Fragment>
<Grid item xs={12}>
<PredefinedList
label="Local Buckets to replicate"
content={bucketsToAlter.join(", ")}
/>
</Grid>
<h4>Remote Endpoint Configuration</h4>
<span className={classes.descriptionText}>
Please avoid the use of root credentials for this feature
</span>
<br />
<br />
<Grid item xs={12}>
<InputBoxWrapper
id="accessKey"
name="accessKey"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAccessKey(e.target.value);
}}
label="Access Key"
value={accessKey}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="secretKey"
name="secretKey"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setSecretKey(e.target.value);
}}
label="Secret Key"
value={secretKey}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="targetURL"
name="targetURL"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setTargetURL(e.target.value);
}}
placeholder="play.min.io:9000"
label="Target URL"
value={targetURL}
/>
</Grid>
<Grid item xs={12}>
<FormSwitchWrapper
checked={useTLS}
id="useTLS"
name="useTLS"
label="Use TLS"
onChange={(e) => {
setUseTLS(e.target.checked);
}}
value="yes"
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="region"
name="region"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setRegion(e.target.value);
}}
label="Region"
value={region}
/>
</Grid>
</Fragment>
),
buttons: [
{
type: "custom",
label: "Next",
enabled: !externalLoading,
action: retrieveRemoteBuckets,
},
],
},
{
label: "Buckets Assignation",
componentRender: (
<Fragment>
<h3>Remote Buckets Assignation</h3>
<span className={classes.descriptionText}>
Please select / type the desired remote bucket were you want
the local data to be replicated.
</span>
<div className={classes.remoteBucketList}>
{bucketsToAlter.map((bucketName: string, index: number) => {
const errorItem = stateOfItem(bucketName);
return (
<Fragment
key={`buckets-assignation-${index.toString()}-${bucketName}`}
>
<div className={errorItem === "" ? classes.hide : ""}>
{bucketName}
</div>
<div className={errorItem === "" ? classes.hide : ""}>
{itemDisplayBulk(index)}
</div>
<div className={errorItem === "" ? classes.hide : ""}>
{responseItem.length > 0 && (
<LogoToShow errString={errorItem} />
)}
</div>
</Fragment>
);
})}
</div>
</Fragment>
),
buttons: [
{
type: "back",
label: "Back",
enabled: true,
},
{
type: "next",
label: "Create",
enabled: !addLoading,
action: addRecord,
},
],
},
]}
forModal
/>
</ModalWrapper>
);
};
const connector = connect(null, {
setModalErrorSnackMessage,
});
export default withStyles(styles)(connector(AddBulkReplicationModal));

View File

@@ -22,6 +22,7 @@ import Grid from "@material-ui/core/Grid";
import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search";
import FileCopyIcon from "@material-ui/icons/FileCopy";
import Moment from "react-moment";
import { Bucket, BucketList, HasPermissionResponse } from "../types";
import { CreateIcon } from "../../../../icons";
@@ -39,6 +40,7 @@ import TableWrapper from "../../Common/TableWrapper/TableWrapper";
import AddBucket from "./AddBucket";
import DeleteBucket from "./DeleteBucket";
import PageHeader from "../../Common/PageHeader/PageHeader";
import BulkReplicationModal from "./BulkReplicationModal";
const styles = (theme: Theme) =>
createStyles({
@@ -94,6 +96,10 @@ const ListBuckets = ({
const [filterBuckets, setFilterBuckets] = useState<string>("");
const [loadingPerms, setLoadingPerms] = useState<boolean>(true);
const [canCreateBucket, setCanCreateBucket] = useState<boolean>(false);
const [selectedBuckets, setSelectedBuckets] = useState<string[]>([]);
const [replicationModalOpen, setReplicationModalOpen] = useState<boolean>(
false
);
// check the permissions for creating bucket
useEffect(() => {
@@ -114,7 +120,7 @@ const ListBuckets = ({
}
const actions = res.permissions ? res.permissions : [];
let canCreate = actions.find((s) => s.id == "createBucket");
let canCreate = actions.find((s) => s.id === "createBucket");
if (canCreate && canCreate.can) {
setCanCreateBucket(true);
} else {
@@ -155,6 +161,7 @@ const ListBuckets = ({
if (refresh) {
setLoading(true);
setSelectedBuckets([]);
}
};
@@ -162,6 +169,7 @@ const ListBuckets = ({
setDeleteOpen(false);
if (refresh) {
setLoading(true);
setSelectedBuckets([]);
}
};
@@ -191,6 +199,33 @@ const ListBuckets = ({
}
});
const selectListBuckets = (e: React.ChangeEvent<HTMLInputElement>) => {
const targetD = e.target;
const value = targetD.value;
const checked = targetD.checked;
let elements: string[] = [...selectedBuckets]; // We clone the selectedBuckets array
if (checked) {
// If the user has checked this field we need to push this to selectedBucketsList
elements.push(value);
} else {
// User has unchecked this field, we need to remove it from the list
elements = elements.filter((element) => element !== value);
}
setSelectedBuckets(elements);
return elements;
};
const closeBulkReplicationModal = (unselectAll: boolean) => {
setReplicationModalOpen(false);
if (unselectAll) {
setSelectedBuckets([]);
}
};
return (
<Fragment>
{addBucketModalOpen && (
@@ -208,6 +243,13 @@ const ListBuckets = ({
}}
/>
)}
{replicationModalOpen && (
<BulkReplicationModal
open={replicationModalOpen}
buckets={selectedBuckets}
closeModalAndRefresh={closeBulkReplicationModal}
/>
)}
<PageHeader label={"Buckets"} />
<Grid container>
<Grid item xs={12} className={classes.container}>
@@ -229,6 +271,17 @@ const ListBuckets = ({
),
}}
/>
<Button
variant="contained"
color="primary"
startIcon={<FileCopyIcon />}
onClick={() => {
setReplicationModalOpen(true);
}}
disabled={selectedBuckets.length === 0}
>
Set Replication
</Button>
{canCreateBucket && (
<Button
variant="contained"
@@ -267,6 +320,8 @@ const ListBuckets = ({
records={filteredRecords}
entityName="Buckets"
idField="name"
selectedItems={selectedBuckets}
onSelect={selectListBuckets}
/>
</Grid>
</Grid>

View File

@@ -195,9 +195,6 @@ const ListObjects = ({
const [selectedObject, setSelectedObject] = useState<string>("");
const [selectedBucket, setSelectedBucket] = useState<string>("");
const [filterObjects, setFilterObjects] = useState<string>("");
const [loadingPromise, setLoadingPromise] = useState<Promise<any> | null>(
null
);
const [loadingStartTime, setLoadingStartTime] = useState<number>(0);
const [loadingMessage, setLoadingMessage] = useState<React.ReactNode>(
defLoading
@@ -269,7 +266,7 @@ const ListObjects = ({
setLoadingStartTime(currentTimestamp);
setLoadingMessage(defLoading);
let p = api
api
.invoke("GET", `/api/v1/buckets/${bucketName}/objects${extraPath}`)
.then((res: BucketObjectsList) => {
setSelectedBucket(bucketName);
@@ -302,7 +299,6 @@ const ListObjects = ({
setLoading(false);
setErrorSnackMessage(err);
});
setLoadingPromise(p);
}
}, [loading, match, setLastAsFile, setErrorSnackMessage]);

View File

@@ -0,0 +1,469 @@
// 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, { useState, useEffect, Fragment } from "react";
import get from "lodash/get";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Button, LinearProgress } from "@material-ui/core";
import Grid from "@material-ui/core/Grid";
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
import { setModalErrorSnackMessage } from "../../../../actions";
import {
ITierResponse,
ITierElement,
} from "../../Configurations/TiersConfiguration/types";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
import api from "../../../../common/api";
import DateSelector from "../../Common/FormComponents/DateSelector/DateSelector";
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
import QueryMultiSelector from "../../Common/FormComponents/QueryMultiSelector/QueryMultiSelector";
import RadioGroupSelector from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
interface IReplicationModal {
open: boolean;
closeModalAndRefresh: (refresh: boolean) => any;
classes: any;
bucketName: string;
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
}
interface ITiersDropDown {
label: string;
value: string;
}
const styles = (theme: Theme) =>
createStyles({
minTableHeader: {
color: "#393939",
"& tr": {
"& th": {
fontWeight: "bold",
},
},
},
buttonContainer: {
textAlign: "right",
},
...modalBasic,
});
const AddLifecycleModal = ({
open,
closeModalAndRefresh,
classes,
bucketName,
setModalErrorSnackMessage,
}: IReplicationModal) => {
const [loadingTiers, setLoadingTiers] = useState<boolean>(true);
const [tiersList, setTiersList] = useState<ITiersDropDown[]>([]);
const [addLoading, setAddLoading] = useState(false);
const [prefix, setPrefix] = useState("");
const [tags, setTags] = useState<string>("");
const [storageClass, setStorageClass] = useState("");
const [NCTransitionSC, setNCTransitionSC] = useState("");
const [expiredObjectDM, setExpiredObjectDM] = useState<boolean>(false);
const [NCExpirationDays, setNCExpirationDays] = useState<string>("0");
const [NCTransitionDays, setNCTransitionDays] = useState<string>("0");
const [ilmType, setIlmType] = useState<string>("expiry");
const [expiryType, setExpiryType] = useState<string>("date");
const [expiryDays, setExpiryDays] = useState<string>("0");
const [expiryDate, setExpiryDate] = useState<string>("");
const [transitionDays, setTransitionDays] = useState<string>("0");
const [transitionDate, setTransitionDate] = useState<string>("");
const [transitionType, setTransitionType] = useState<string>("date");
const [isFormValid, setIsFormValid] = useState<boolean>(false);
useEffect(() => {
if (loadingTiers) {
api
.invoke("GET", `/api/v1/admin/tiers`)
.then((res: ITierResponse) => {
const tiersList: ITierElement[] | null = get(res, "items", []);
if (tiersList !== null && tiersList.length >= 1) {
const objList = tiersList.map((tier: ITierElement) => {
const tierType = tier.type;
const value = get(tier, `${tierType}.name`, "");
return { label: value, value: value };
});
setTiersList(objList);
if (objList.length > 0) {
setStorageClass(objList[0].value);
}
}
setLoadingTiers(false);
})
.catch((err) => {
setLoadingTiers(false);
});
}
}, [loadingTiers]);
useEffect(() => {
let valid = true;
if (ilmType === "expiry") {
if (expiryType === "date" && expiryDate === "") {
valid = false;
}
if (expiryType === "days" && parseInt(expiryDays) < 1) {
valid = false;
}
} else {
if (transitionType === "date" && transitionDate === "") {
valid = false;
}
if (transitionType === "days" && parseInt(transitionDays) < 1) {
valid = false;
}
if (storageClass === "") {
valid = false;
}
}
setIsFormValid(valid);
}, [
ilmType,
expiryType,
expiryDate,
expiryDays,
transitionType,
transitionDate,
transitionDays,
storageClass,
]);
const addRecord = () => {
let rules = {};
if (ilmType === "expiry") {
let expiry = {};
if (expiryType === "date") {
expiry = {
expiry_date: `${expiryDate}T23:59:59Z`,
};
} else {
expiry = {
expiry_days: parseInt(expiryDays),
};
}
rules = {
...expiry,
noncurrentversion_expiration_days: parseInt(NCExpirationDays),
};
} else {
let transition = {};
if (transitionType === "date") {
transition = {
transition_date: `${transitionDate}T23:59:59Z`,
};
} else {
transition = {
transition_days: parseInt(transitionDays),
};
}
rules = {
...transition,
noncurrentversion_transition_days: parseInt(NCTransitionDays),
noncurrentversion_transition_storage_class: NCTransitionSC,
storage_class: storageClass,
};
}
const lifecycleInsert = {
prefix,
tags,
expired_object_delete_marker: expiredObjectDM,
...rules,
};
api
.invoke(
"POST",
`/api/v1/buckets/${bucketName}/lifecycle`,
lifecycleInsert
)
.then(() => {
setAddLoading(false);
closeModalAndRefresh(true);
})
.catch((err) => {
setAddLoading(false);
setModalErrorSnackMessage(err);
});
};
return (
<ModalWrapper
modalOpen={open}
onClose={() => {
closeModalAndRefresh(false);
}}
title="Add Lifecycle Rule"
>
{loadingTiers && (
<Grid container className={classes.loadingBox}>
<Grid item xs={12}>
<LinearProgress />
</Grid>
</Grid>
)}
{!loadingTiers && (
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setAddLoading(true);
addRecord();
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
<h3>Lifecycle Configuration</h3>
<Grid item xs={12}>
<RadioGroupSelector
currentSelection={ilmType}
id="quota_type"
name="quota_type"
label="ILM Rule"
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
setIlmType(e.target.value as string);
}}
selectorOptions={[
{ value: "expiry", label: "Expiry" },
{ value: "transition", label: "Transition" },
]}
/>
</Grid>
{ilmType === "expiry" ? (
<Fragment>
<Grid item xs={12}>
<RadioGroupSelector
currentSelection={expiryType}
id="expiryType"
name="expiryType"
label="Expiry Type"
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
setExpiryType(e.target.value as string);
}}
selectorOptions={[
{ value: "date", label: "Date" },
{ value: "days", label: "Days" },
]}
/>
</Grid>
<Grid item xs={12}>
{expiryType === "date" ? (
<DateSelector
id="expiry_date"
label="Expiry Date"
value={expiryDate}
borderBottom={true}
onDateChange={(date: string, isValid: boolean) => {
if (isValid) {
setExpiryDate(date);
}
}}
/>
) : (
<InputBoxWrapper
type="number"
id="expiry_days"
name="expiry_days"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setExpiryDays(e.target.value);
}}
label="Expiry Days"
value={expiryDays}
min="0"
/>
)}
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
type="number"
id="noncurrentversion_expiration_days"
name="noncurrentversion_expiration_days"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setNCExpirationDays(e.target.value);
}}
label="Non-current Expiration Days"
value={NCExpirationDays}
min="0"
/>
</Grid>
</Fragment>
) : (
<Fragment>
<Grid item xs={12}>
<RadioGroupSelector
currentSelection={transitionType}
id="transitionType"
name="transitionType"
label="Transition Type"
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
setTransitionType(e.target.value as string);
}}
selectorOptions={[
{ value: "date", label: "Date" },
{ value: "days", label: "Days" },
]}
/>
</Grid>
<Grid item xs={12}>
{transitionType === "date" ? (
<DateSelector
id="transition_date"
label="Transition Date"
value={transitionDate}
borderBottom={true}
onDateChange={(date: string, isValid: boolean) => {
if (isValid) {
setTransitionDate(date);
}
}}
/>
) : (
<InputBoxWrapper
type="number"
id="transition_days"
name="transition_days"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setTransitionDays(e.target.value);
}}
label="Transition Days"
value={transitionDays}
min="0"
/>
)}
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
type="number"
id="noncurrentversion_transition_days"
name="noncurrentversion_transition_days"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setNCTransitionDays(e.target.value);
}}
label="Non-current Transition Days"
value={NCTransitionDays}
min="0"
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="noncurrentversion_t_SC"
name="noncurrentversion_t_SC"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setNCTransitionSC(e.target.value);
}}
placeholder="Set Non-current Version Transition Storage Class"
label="Non-current Version Transition Storage Class"
value={NCTransitionSC}
/>
</Grid>
<Grid item xs={12}>
<SelectWrapper
label="Storage Class"
id="storage_class"
name="storage_class"
value={storageClass}
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
setStorageClass(e.target.value as string);
}}
options={tiersList}
/>
</Grid>
</Fragment>
)}
<h3>File Configuration</h3>
<Grid item xs={12}>
<InputBoxWrapper
id="prefix"
name="prefix"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setPrefix(e.target.value);
}}
label="Prefix"
value={prefix}
/>
</Grid>
<Grid item xs={12}>
<QueryMultiSelector
name="tags"
label="Tags"
elements={""}
onChange={(vl: string) => {
setTags(vl);
}}
keyPlaceholder="Tag Key"
valuePlaceholder="Tag Value"
withBorder
/>
</Grid>
<Grid item xs={12}>
<FormSwitchWrapper
value="expired_delete_marker"
id="expired_delete_marker"
name="expired_delete_marker"
checked={expiredObjectDM}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
setExpiredObjectDM(event.target.checked);
}}
label={"Expired Object Delete Marker"}
indicatorLabels={["On", "Off"]}
/>
</Grid>
</Grid>
<Grid item xs={12} className={classes.buttonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={addLoading || !isFormValid}
>
Save
</Button>
</Grid>
{addLoading && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</Grid>
</form>
)}
</ModalWrapper>
);
};
const connector = connect(null, {
setModalErrorSnackMessage,
});
export default withStyles(styles)(connector(AddLifecycleModal));

View File

@@ -21,7 +21,7 @@ import { Button, LinearProgress } from "@material-ui/core";
import get from "lodash/get";
import Grid from "@material-ui/core/Grid";
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
import { IRemoteBucket } from "../types";
import { BulkReplicationResponse } from "../types";
import { setModalErrorSnackMessage } from "../../../../actions";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
@@ -66,52 +66,43 @@ const AddReplicationModal = ({
const [region, setRegion] = useState("");
const addRecord = () => {
const remoteBucketInfo = {
const replicate = [
{
originBucket: bucketName,
destinationBucket: targetBucket,
},
];
const remoteBucketsInfo = {
accessKey: accessKey,
secretKey: secretKey,
sourceBucket: bucketName,
targetURL: targetURL,
targetBucket: targetBucket,
region: region,
bucketsRelation: replicate,
};
api
.invoke("POST", "/api/v1/remote-buckets", remoteBucketInfo)
.then(() => {
api
.invoke("GET", "/api/v1/remote-buckets")
.then((res: any) => {
const remoteBuckets = get(res, "buckets", []);
const remoteBucket = remoteBuckets.find(
(itemRemote: IRemoteBucket) => {
return itemRemote.sourceBucket === bucketName;
}
);
if (remoteBucket && remoteBucket.remoteARN) {
const remoteARN = remoteBucket.remoteARN;
const replicationInfo = {
destination_bucket: targetBucket,
arn: remoteARN,
};
api
.invoke(
"POST",
`/api/v1/buckets/${bucketName}/replication`,
replicationInfo
)
.then(() => {
setAddLoading(false);
closeModalAndRefresh();
})
.catch((err) => {
setAddLoading(false);
setModalErrorSnackMessage(err);
});
}
})
.catch((err) => {
setModalErrorSnackMessage(err);
});
.invoke("POST", "/api/v1/buckets-replication", remoteBucketsInfo)
.then((response: BulkReplicationResponse) => {
setAddLoading(false);
const states = get(response, "replicationState", []);
if (states.length > 0) {
const itemVal = states[0];
setAddLoading(false);
if (itemVal.errorString && itemVal.errorString !== "") {
setModalErrorSnackMessage(itemVal.errorString);
return;
}
closeModalAndRefresh();
return;
}
setModalErrorSnackMessage("No changes applied");
})
.catch((err) => {
setAddLoading(false);

View File

@@ -93,7 +93,7 @@ const DeleteEvent = ({
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete the this event?
Are you sure you want to delete this event?
</DialogContentText>
</DialogContent>
<DialogActions>

View File

@@ -0,0 +1,117 @@
// 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, { useState } from "react";
import { connect } from "react-redux";
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
LinearProgress,
} from "@material-ui/core";
import { setErrorSnackMessage } from "../../../../actions";
import api from "../../../../common/api";
interface IDeleteReplicationProps {
closeDeleteModalAndRefresh: (refresh: boolean) => void;
deleteOpen: boolean;
selectedBucket: string;
ruleToDelete: string;
setErrorSnackMessage: typeof setErrorSnackMessage;
}
const DeleteReplicationRule = ({
closeDeleteModalAndRefresh,
deleteOpen,
selectedBucket,
ruleToDelete,
setErrorSnackMessage,
}: IDeleteReplicationProps) => {
const [deleteLoading, setDeleteLoading] = useState<boolean>(false);
const removeRecord = () => {
if (!deleteLoading) {
setDeleteLoading(true);
api
.invoke(
"DELETE",
`/api/v1/buckets/${selectedBucket}/replication/${ruleToDelete}`
)
.then(() => {
setDeleteLoading(false);
closeDeleteModalAndRefresh(true);
})
.catch((err) => {
setDeleteLoading(false);
setErrorSnackMessage(err);
});
}
};
return (
<Dialog
open={deleteOpen}
onClose={() => {
closeDeleteModalAndRefresh(false);
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete Replication Rule</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete replication rule <b>{ruleToDelete}</b>
? <br />
Remember, at lease one rule must be present once replication has been
enabled
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
closeDeleteModalAndRefresh(false);
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button
onClick={() => {
removeRecord();
}}
color="secondary"
autoFocus
>
Delete
</Button>
</DialogActions>
</Dialog>
);
};
const mapDispatchToProps = {
setErrorSnackMessage,
};
const connector = connect(null, mapDispatchToProps);
export default connector(DeleteReplicationRule);

View File

@@ -0,0 +1,197 @@
// 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, useState } from "react";
import { connect } from "react-redux";
import Grid from "@material-ui/core/Grid";
import { Button, LinearProgress } from "@material-ui/core";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
import { setModalErrorSnackMessage } from "../../../../actions";
import api from "../../../../common/api";
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import QueryMultiSelector from "../../Common/FormComponents/QueryMultiSelector/QueryMultiSelector";
import { LifeCycleItem } from "../types";
const styles = (theme: Theme) =>
createStyles({
strongText: {
fontWeight: 700,
},
keyName: {
marginLeft: 5,
},
buttonContainer: {
textAlign: "right",
},
...modalBasic,
});
interface IAddUserContentProps {
classes: any;
closeModalAndRefresh: (reload: boolean) => void;
selectedBucket: string;
lifecycle: LifeCycleItem;
open: boolean;
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
}
const EditLifecycleConfiguration = ({
classes,
closeModalAndRefresh,
selectedBucket,
lifecycle,
open,
setModalErrorSnackMessage,
}: IAddUserContentProps) => {
const [addLoading, setAddLoading] = useState<boolean>(false);
const [tags, setTags] = useState<string>("");
const [enabled, setEnabled] = useState<boolean>(false);
useEffect(() => {
if (lifecycle.status === "Enabled") {
setEnabled(true);
}
if (lifecycle.tags) {
const tgs = lifecycle.tags.reduce(
(stringLab: string, currItem: any, index: number) => {
return `${stringLab}${index !== 0 ? "&" : ""}${currItem.key}=${
currItem.value
}`;
},
""
);
setTags(tgs);
}
}, [lifecycle]);
const saveRecord = (event: React.FormEvent) => {
event.preventDefault();
if (addLoading) {
return;
}
setAddLoading(true);
if (selectedBucket !== null && lifecycle !== null) {
api
.invoke(
"PUT",
`/api/v1/buckets/${selectedBucket}/lifecycle/${lifecycle.id}`,
{
disable: !enabled,
tags: tags,
}
)
.then((res) => {
setAddLoading(false);
closeModalAndRefresh(true);
})
.catch((err) => {
setAddLoading(false);
setModalErrorSnackMessage(err);
});
}
};
return (
<ModalWrapper
onClose={() => {
closeModalAndRefresh(false);
}}
modalOpen={open}
title={"Edit Lifecycle Configuration"}
>
<div className={classes.floatingEnabled}>
<FormSwitchWrapper
indicatorLabels={["Enabled", "Disabled"]}
checked={enabled}
value={"user_enabled"}
id="user-status"
name="user-status"
onChange={(e) => {
setEnabled(e.target.checked);
}}
switchOnly
/>
</div>
<React.Fragment>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
saveRecord(e);
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
<Grid item xs={12}>
<InputBoxWrapper
id="id"
name="id"
label="Id"
value={lifecycle.id}
onChange={() => {}}
disabled
/>
</Grid>
<Grid item xs={12}>
<QueryMultiSelector
name="tags"
label="Tags"
elements={tags}
onChange={(vl: string) => {
setTags(vl);
}}
keyPlaceholder="Tag Key"
valuePlaceholder="Tag Value"
withBorder
/>
</Grid>
</Grid>
<Grid item xs={12} className={classes.buttonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={addLoading}
>
Save
</Button>
</Grid>
{addLoading && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</Grid>
</form>
</React.Fragment>
</ModalWrapper>
);
};
const mapDispatchToProps = {
setModalErrorSnackMessage,
};
const connector = connect(null, mapDispatchToProps);
export default withStyles(styles)(connector(EditLifecycleConfiguration));

View File

@@ -18,6 +18,7 @@ import React, { useEffect, useState, Fragment } from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Button, IconButton } from "@material-ui/core";
import * as reactMoment from "react-moment";
import get from "lodash/get";
import Paper from "@material-ui/core/Paper";
import Grid from "@material-ui/core/Grid";
@@ -25,6 +26,7 @@ import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import CircularProgress from "@material-ui/core/CircularProgress";
import Checkbox from "@material-ui/core/Checkbox";
import Typography from "@material-ui/core/Typography";
import api from "../../../../common/api";
import {
BucketEncryptionInfo,
@@ -39,11 +41,17 @@ import {
BucketReplicationRuleDeleteMarker,
BucketVersioning,
HasPermissionResponse,
LifeCycleItem,
} from "../types";
import { CreateIcon } from "../../../../icons";
import { niceBytes } from "../../../../common/utils";
import { containerForHeader } from "../../Common/FormComponents/common/styleLibrary";
import { setErrorSnackMessage } from "../../../../actions";
import { Policy } from "../../Policies/types";
import { User } from "../../Users/types";
import { AppState } from "../../../../store";
import { ISessionResponse } from "../../types";
import SetAccessPolicy from "./SetAccessPolicy";
import SetRetentionConfig from "./SetRetentionConfig";
import AddEvent from "./AddEvent";
import DeleteEvent from "./DeleteEvent";
@@ -53,11 +61,11 @@ import PageHeader from "../../Common/PageHeader/PageHeader";
import EnableBucketEncryption from "./EnableBucketEncryption";
import PencilIcon from "../../Common/TableWrapper/TableActionIcons/PencilIcon";
import EnableVersioningModal from "./EnableVersioningModal";
import Typography from "@material-ui/core/Typography";
import UsageIcon from "../../../../icons/UsageIcon";
import AddPolicy from "../../Policies/AddPolicy";
import SetAccessPolicy from "./SetAccessPolicy";
import { Policy } from "../../Policies/types";
import DeleteReplicationRule from "../ViewBucket/DeleteReplicationRule";
import EditLifecycleConfiguration from "./EditLifecycleConfiguration";
import AddLifecycleModal from "./AddLifecycleModal";
const styles = (theme: Theme) =>
createStyles({
@@ -132,7 +140,8 @@ const styles = (theme: Theme) =>
},
paperContainer: {
padding: 15,
paddingLeft: 23,
paddingLeft: 50,
display: "flex",
},
headerContainer: {
display: "flex",
@@ -178,6 +187,7 @@ interface IViewBucketProps {
classes: any;
match: any;
setErrorSnackMessage: typeof setErrorSnackMessage;
session: ISessionResponse;
}
interface TabPanelProps {
@@ -214,6 +224,7 @@ const ViewBucket = ({
classes,
match,
setErrorSnackMessage,
session,
}: IViewBucketProps) => {
const [info, setInfo] = useState<BucketInfo | null>(null);
const [records, setRecords] = useState<BucketEvent[]>([]);
@@ -222,6 +233,8 @@ const ViewBucket = ({
>([]);
const [bucketPolicy, setBucketPolicy] = useState<Policy[]>([]);
const [loadingPolicy, setLoadingPolicy] = useState<boolean>(true);
const [bucketUsers, setBucketUsers] = useState<User[]>([]);
const [loadingUsers, setLoadingUsers] = useState<boolean>(true);
const [loadingBucket, setLoadingBucket] = useState<boolean>(true);
const [loadingEvents, setLoadingEvents] = useState<boolean>(true);
const [loadingVersioning, setLoadingVersioning] = useState<boolean>(true);
@@ -240,6 +253,7 @@ const ViewBucket = ({
setEnableEncryptionScreenOpen,
] = useState<boolean>(false);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [editLifecycleOpen, setEditLifecycleOpen] = useState<boolean>(false);
const [selectedEvent, setSelectedEvent] = useState<BucketEvent | null>(null);
const [bucketSize, setBucketSize] = useState<string>("0");
const [openSetReplication, setOpenSetReplication] = useState<boolean>(false);
@@ -256,6 +270,16 @@ const ViewBucket = ({
const [loadingPerms, setLoadingPerms] = useState<boolean>(true);
const [canPutReplication, setCanPutReplication] = useState<boolean>(false);
const [canGetReplication, setCanGetReplication] = useState<boolean>(false);
const [deleteReplicationModal, setDeleteReplicationModal] = useState<boolean>(
false
);
const [selectedRRule, setSelectedRRule] = useState<string>("");
const [loadingLifecycle, setLoadingLifecycle] = useState<boolean>(true);
const [lifecycleRecords, setLifecycleRecords] = useState<LifeCycleItem[]>([]);
const [addLifecycleOpen, setAddLifecycleOpen] = useState<boolean>(false);
const bucketName = match.params["bucketName"];
const ilmEnabled = session.features?.indexOf("ilm") > -1;
// check the permissions for creating bucket
useEffect(() => {
@@ -283,7 +307,7 @@ const ViewBucket = ({
const actions = res.permissions ? res.permissions : [];
let canPutReplication = actions.find(
(s) => s.id == "PutReplicationConfiguration"
(s) => s.id === "PutReplicationConfiguration"
);
if (canPutReplication && canPutReplication.can) {
@@ -292,7 +316,7 @@ const ViewBucket = ({
setCanPutReplication(false);
}
let canGetReplication = actions.find(
(s) => s.id == "GetReplicationConfiguration"
(s) => s.id === "GetReplicationConfiguration"
);
if (canGetReplication && canGetReplication.can) {
@@ -308,9 +332,7 @@ const ViewBucket = ({
setErrorSnackMessage(err);
});
}
}, [loadingPerms, setErrorSnackMessage]);
const bucketName = match.params["bucketName"];
}, [bucketName, loadingPerms, setErrorSnackMessage]);
useEffect(() => {
if (loadingEvents) {
@@ -356,7 +378,12 @@ const ViewBucket = ({
setLoadingLocking(false);
});
}
}, [loadingObjectLocking, setErrorSnackMessage, bucketName]);
}, [
loadingObjectLocking,
setErrorSnackMessage,
bucketName,
loadingVersioning,
]);
useEffect(() => {
if (loadingReplication) {
@@ -389,6 +416,21 @@ const ViewBucket = ({
}
}, [loadingPolicy, setErrorSnackMessage, bucketName]);
useEffect(() => {
if (loadingUsers) {
api
.invoke("GET", `/api/v1/bucket-users/${bucketName}`)
.then((res: any) => {
setBucketUsers(res);
setLoadingUsers(false);
})
.catch((err: any) => {
setErrorSnackMessage(err);
setLoadingUsers(false);
});
}
}, [loadingUsers, setErrorSnackMessage, bucketName]);
useEffect(() => {
if (loadingSize) {
api
@@ -446,6 +488,22 @@ const ViewBucket = ({
const setBucketVersioning = () => {
setEnableVersioningOpen(true);
};
useEffect(() => {
if (loadingLifecycle) {
api
.invoke("GET", `/api/v1/buckets/${bucketName}/lifecycle`)
.then((res: any) => {
const records = get(res, "lifecycle", []);
setLifecycleRecords(records || []);
setLoadingLifecycle(false);
})
.catch((err) => {
console.error(err);
setLoadingLifecycle(false);
});
}
}, [loadingLifecycle, setLoadingLifecycle, bucketName]);
const loadAllBucketData = () => {
setLoadingBucket(true);
@@ -500,11 +558,23 @@ const ViewBucket = ({
}
};
const closeEditLCAndRefresh = (refresh: boolean) => {
setEditLifecycleOpen(false);
if (refresh) {
setLoadingLifecycle(true);
}
};
const confirmDeleteEvent = (evnt: BucketEvent) => {
setDeleteOpen(true);
setSelectedEvent(evnt);
};
const confirmDeleteReplication = (replication: BucketReplicationRule) => {
setSelectedRRule(replication.id);
setDeleteReplicationModal(true);
};
const closeEnableVersioning = (refresh: boolean) => {
setEnableVersioningOpen(false);
if (refresh) {
@@ -527,13 +597,28 @@ const ViewBucket = ({
};
const ruleDelDisplay = (events: BucketReplicationRuleDeleteMarker) => {
return <Fragment>{events.status}</Fragment>;
return null;
};
const setOpenReplicationOpen = (open = false) => {
setOpenSetReplication(open);
};
const closeReplicationModalDelete = (refresh: boolean) => {
setDeleteReplicationModal(false);
if (refresh) {
setLoadingReplication(true);
}
};
const closeAddLCAndRefresh = (refresh: boolean) => {
setAddLifecycleOpen(false);
if (refresh) {
setLoadingLifecycle(true);
}
};
const handleEncryptionCheckbox = (
event: React.ChangeEvent<HTMLInputElement>
) => {
@@ -558,6 +643,71 @@ const ViewBucket = ({
};
const PolicyActions = [{ type: "view", onClick: viewAction }];
const replicationTableActions = [
{
type: "delete",
onClick: confirmDeleteReplication,
disableButtonFunction: () => replicationRules.length <= 1,
},
];
const expirationRender = (expiration: any) => {
if (expiration.days) {
return `${expiration.days} day${expiration.days > 1 ? "s" : ""}`;
}
if (expiration.date === "0001-01-01T00:00:00Z") {
return "";
}
return <reactMoment.default>{expiration.date}</reactMoment.default>;
};
const transitionRender = (transition: any) => {
if (transition.days) {
return `${transition.days} day${transition.days > 1 ? "s" : ""}`;
}
if (transition.date === "0001-01-01T00:00:00Z") {
return "";
}
return <reactMoment.default>{transition.date}</reactMoment.default>;
};
const renderStorageClass = (objectST: any) => {
const stClass = get(objectST, "transition.storage_class", "");
return stClass;
};
const lifecycleColumns = [
{ label: "ID", elementKey: "id" },
{
label: "Prefix",
elementKey: "prefix",
},
{
label: "Status",
elementKey: "status",
},
{
label: "Expiration",
elementKey: "expiration",
renderFunction: expirationRender,
},
{
label: "Transition",
elementKey: "transition",
renderFunction: transitionRender,
},
{
label: "Storage Class",
elementKey: "storage_class",
renderFunction: renderStorageClass,
renderFullObject: true,
},
];
return (
<Fragment>
@@ -620,6 +770,32 @@ const ViewBucket = ({
versioningCurrentState={isVersioned}
/>
)}
{deleteReplicationModal && (
<DeleteReplicationRule
deleteOpen={deleteReplicationModal}
selectedBucket={bucketName}
closeDeleteModalAndRefresh={closeReplicationModalDelete}
ruleToDelete={selectedRRule}
/>
)}
{editLifecycleOpen && (
<EditLifecycleConfiguration
open={editLifecycleOpen}
closeModalAndRefresh={closeEditLCAndRefresh}
selectedBucket={bucketName}
lifecycle={{
id: "",
}}
/>
)}
{addLifecycleOpen && (
<AddLifecycleModal
open={addLifecycleOpen}
bucketName={bucketName}
closeModalAndRefresh={closeAddLCAndRefresh}
/>
)}
<PageHeader label={`Bucket > ${match.params["bucketName"]}`} />
<Grid container>
<Grid item xs={12} className={classes.container}>
@@ -661,7 +837,9 @@ const ViewBucket = ({
aria-label="access"
size="small"
className={classes.propertiesIcon}
onClick={setBucketVersioning}
onClick={() => {
setAccessPolicyScreenOpen(true);
}}
>
<PencilIcon active={true} />
</IconButton>
@@ -689,11 +867,7 @@ const ViewBucket = ({
/>
</div>
</div>
</Paper>
</Grid>
{hasObjectLocking && (
<Grid item>
<Paper className={classes.paperContainer}>
{hasObjectLocking && (
<div className={classes.gridContainer}>
<div>Object Locking</div>
<div></div>
@@ -714,9 +888,7 @@ const ViewBucket = ({
aria-label="retention"
size="small"
className={classes.propertiesIcon}
onClick={() => {
setAccessPolicyScreenOpen(true);
}}
onClick={setBucketVersioning}
>
<PencilIcon active={true} />
</IconButton>
@@ -749,16 +921,16 @@ const ViewBucket = ({
)}
</div>
</div>
</Paper>
</Grid>
)}
)}
</Paper>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid container item xs={12}>
<Grid item xs={6}>
<Grid item xs={9}>
<Tabs
value={curTab}
onChange={(e: React.ChangeEvent<{}>, newValue: number) => {
@@ -767,15 +939,19 @@ const ViewBucket = ({
indicatorColor="primary"
textColor="primary"
aria-label="cluster-tabs"
variant="scrollable"
scrollButtons="auto"
>
<Tab label="Events" {...a11yProps(0)} />
{canGetReplication && (
<Tab label="Replication" {...a11yProps(1)} />
)}
<Tab label="Policies" {...a11yProps(2)} />
<Tab label="Users" {...a11yProps(3)} />
{ilmEnabled && <Tab label="Lifecycle" {...a11yProps(4)} />}
</Tabs>
</Grid>
<Grid item xs={6} className={classes.actionsTray}>
<Grid item xs={3} className={classes.actionsTray}>
{curTab === 0 && (
<Button
variant="contained"
@@ -806,6 +982,19 @@ const ViewBucket = ({
)}
</Fragment>
)}
{curTab === 4 && (
<Button
variant="contained"
color="primary"
startIcon={<CreateIcon />}
size="medium"
onClick={() => {
setAddLifecycleOpen(true);
}}
>
Add Lifecycle Rule
</Button>
)}
</Grid>
</Grid>
<Grid item xs={12}>
@@ -831,7 +1020,7 @@ const ViewBucket = ({
{canGetReplication && (
<TabPanel index={1} value={curTab}>
<TableWrapper
itemActions={tableActions}
itemActions={replicationTableActions}
columns={[
{ label: "ID", elementKey: "id" },
{
@@ -844,7 +1033,7 @@ const ViewBucket = ({
renderFunction: ruleDestDisplay,
},
{
label: "Delete Replication",
label: "Delete Marker Replication",
elementKey: "delete_marker_replication",
renderFunction: ruleDelDisplay,
},
@@ -867,6 +1056,26 @@ const ViewBucket = ({
idField="name"
/>
</TabPanel>
<TabPanel index={3} value={curTab}>
<TableWrapper
columns={[{ label: "User", elementKey: "accessKey" }]}
isLoading={loadingUsers}
records={bucketUsers}
entityName="Users"
idField="accessKey"
/>
</TabPanel>
<TabPanel index={4} value={curTab}>
<TableWrapper
itemActions={[]}
columns={lifecycleColumns}
isLoading={loadingLifecycle}
records={lifecycleRecords}
entityName="Lifecycle"
customEmptyMessage="There are no Lifecycle rules yet"
idField="id"
/>
</TabPanel>
</Grid>
</Grid>
</Grid>
@@ -874,7 +1083,11 @@ const ViewBucket = ({
);
};
const connector = connect(null, {
const mapState = (state: AppState) => ({
session: state.console.session,
});
const connector = connect(mapState, {
setErrorSnackMessage,
});

View File

@@ -47,6 +47,11 @@ export interface BucketEventList {
total: number;
}
export interface BucketPolicy {
name: string;
body: string;
}
export interface ArnList {
arns: string[];
}
@@ -127,3 +132,33 @@ export interface PermissionAction {
export interface HasPermissionResponse {
permissions: PermissionAction[];
}
export interface BulkReplicationResponse {
replicationState: BulkReplicationItem[];
}
export interface BulkReplicationItem {
errorString: string;
originBucket: string;
targetBucket: string;
}
interface IExpirationLifecycle {
days: number;
date: string;
}
interface ITransitionLifecycle {
days: number;
date: string;
storage_class?: string;
}
export interface LifeCycleItem {
id: string;
prefix?: string;
expiration?: IExpirationLifecycle;
transition?: ITransitionLifecycle;
tags?: any;
status?: string;
}

View File

@@ -104,13 +104,13 @@ const styles = (theme: Theme) =>
borderBottom: "#9c9c9c 1px solid",
paddingBottom: 14,
marginBottom: 20,
maxWidth: 840,
},
wrapperContainer: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
maxWidth: 840,
margin: "0px auto",
},
indicatorLabel: {
fontSize: 12,
@@ -118,9 +118,6 @@ const styles = (theme: Theme) =>
color: "#081C42",
margin: "0 8px 0 10px",
},
switchContainer: {
display: "flex",
},
fieldDescription: {
marginTop: 4,
color: "#999999",

View File

@@ -0,0 +1,251 @@
// 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, {
useState,
useEffect,
createRef,
useLayoutEffect,
ChangeEvent,
useRef,
} from "react";
import get from "lodash/get";
import debounce from "lodash/debounce";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import HelpIcon from "@material-ui/icons/Help";
import { InputLabel, Tooltip } from "@material-ui/core";
import { fieldBasic, tooltipHelper } from "../common/styleLibrary";
import InputBoxWrapper from "../InputBoxWrapper/InputBoxWrapper";
import AddIcon from "../../../../../icons/AddIcon";
interface IQueryMultiSelector {
elements: string;
name: string;
label: string;
tooltip?: string;
keyPlaceholder?: string;
valuePlaceholder?: string;
classes: any;
withBorder?: boolean;
onChange: (elements: string) => void;
}
const styles = (theme: Theme) =>
createStyles({
...fieldBasic,
...tooltipHelper,
inputWithBorder: {
border: "1px solid #EAEAEA",
padding: 15,
height: 150,
overflowY: "auto",
position: "relative",
marginTop: 15,
},
labelContainer: {
display: "flex",
},
lineInputBoxes: {
display: "flex",
},
queryDiv: {
alignSelf: "center",
margin: "0 4px",
fontWeight: 600,
},
});
const QueryMultiSelector = ({
elements,
name,
label,
tooltip = "",
keyPlaceholder = "",
valuePlaceholder = "",
onChange,
withBorder = false,
classes,
}: IQueryMultiSelector) => {
const [currentKeys, setCurrentKeys] = useState<string[]>([""]);
const [currentValues, setCurrentValues] = useState<string[]>([""]);
const bottomList = createRef<HTMLDivElement>();
// Use effect to get the initial values from props
useEffect(() => {
if (
currentKeys.length === 1 &&
currentKeys[0] === "" &&
currentValues.length === 1 &&
currentValues[0] === "" &&
elements &&
elements !== ""
) {
const elementsSplit = elements.split("&");
let keys = [];
let values = [];
elementsSplit.forEach((element: string) => {
const splittedVals = element.split("=");
if (splittedVals.length === 2) {
keys.push(splittedVals[0]);
values.push(splittedVals[1]);
}
});
keys.push("");
values.push("");
setCurrentKeys(keys);
setCurrentValues(values);
}
}, [currentKeys, currentValues, elements]);
// Use effect to send new values to onChange
useEffect(() => {
const refScroll = bottomList.current;
if (refScroll) {
refScroll.scrollIntoView(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentKeys]);
// We avoid multiple re-renders / hang issue typing too fast
const firstUpdate = useRef(true);
useLayoutEffect(() => {
if (firstUpdate.current) {
firstUpdate.current = false;
return;
}
debouncedOnChange();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentKeys, currentValues]);
// If the last input is not empty, we add a new one
const addEmptyLine = () => {
if (
currentKeys[currentKeys.length - 1].trim() !== "" &&
currentValues[currentValues.length - 1].trim() !== ""
) {
const keysList = [...currentKeys];
const valuesList = [...currentValues];
keysList.push("");
valuesList.push("");
setCurrentKeys(keysList);
setCurrentValues(valuesList);
}
};
// Onchange function for input box, we get the dataset-index & only update that value in the array
const onChangeKey = (e: ChangeEvent<HTMLInputElement>) => {
e.persist();
let updatedElement = [...currentKeys];
const index = get(e.target, "dataset.index", 0);
updatedElement[index] = e.target.value;
setCurrentKeys(updatedElement);
};
const onChangeValue = (e: ChangeEvent<HTMLInputElement>) => {
e.persist();
let updatedElement = [...currentValues];
const index = get(e.target, "dataset.index", 0);
updatedElement[index] = e.target.value;
setCurrentValues(updatedElement);
};
// Debounce for On Change
const debouncedOnChange = debounce(() => {
let queryString = "";
currentKeys.forEach((keyVal, index) => {
if (currentKeys[index] && currentValues[index]) {
let insertString = `${keyVal}=${currentValues[index]}`;
if (index !== 0) {
insertString = `&${insertString}`;
}
queryString = `${queryString}${insertString}`;
}
});
onChange(queryString);
}, 500);
const inputs = currentValues.map((element, index) => {
return (
<Grid
item
xs={12}
className={classes.lineInputBoxes}
key={`query-pair-${name}-${index.toString()}`}
>
<InputBoxWrapper
id={`${name}-key-${index.toString()}`}
label={""}
name={`${name}-${index.toString()}`}
value={currentKeys[index]}
onChange={onChangeKey}
index={index}
placeholder={keyPlaceholder}
/>
<span className={classes.queryDiv}>:</span>
<InputBoxWrapper
id={`${name}-value-${index.toString()}`}
label={""}
name={`${name}-${index.toString()}`}
value={currentValues[index]}
onChange={onChangeValue}
index={index}
placeholder={valuePlaceholder}
overlayIcon={index === currentValues.length - 1 ? <AddIcon /> : null}
overlayAction={() => {
addEmptyLine();
}}
/>
</Grid>
);
});
return (
<React.Fragment>
<Grid item xs={12} className={classes.fieldContainer}>
<InputLabel className={classes.inputLabel}>
<span>{label}</span>
{tooltip !== "" && (
<div className={classes.tooltipContainer}>
<Tooltip title={tooltip} placement="top-start">
<HelpIcon className={classes.tooltip} />
</Tooltip>
</div>
)}
</InputLabel>
<Grid
item
xs={12}
className={`${withBorder ? classes.inputWithBorder : ""}`}
>
{inputs}
<div ref={bottomList} />
</Grid>
</Grid>
</React.Fragment>
);
};
export default withStyles(styles)(QueryMultiSelector);

View File

@@ -37,13 +37,16 @@ export const fieldBasic = {
marginBottom: 20,
position: "relative" as const,
maxWidth: 840,
margin: "0px auto",
},
tooltipContainer: {
marginLeft: 5,
display: "flex",
alignItems: "center",
},
switchContainer: {
display: "flex",
maxWidth: 840,
},
};
export const modalBasic = {
@@ -80,7 +83,6 @@ export const modalBasic = {
overflowY: "auto" as const,
height: 170,
maxWidth: 840,
margin: "0 auto",
},
};
@@ -191,7 +193,6 @@ export const predefinedList = {
prefinedContainer: {
maxWidth: 840,
width: "100%",
margin: "0 auto",
},
predefinedTitle: {
fontSize: 16,
@@ -285,6 +286,10 @@ export const settingsCommon = {
padding: "15px 38px",
textAlign: "right" as const,
},
innerSettingsButtonContainer: {
maxWidth: 840,
textAlign: "right" as const,
},
settingsOptionsContainer: {
height: "calc(100vh - 244px)",
backgroundColor: "#fff",
@@ -315,6 +320,56 @@ export const settingsCommon = {
},
};
export const typesSelection = {
iconContainer: {
display: "flex" as const,
flexDirection: "row" as const,
maxWidth: 455,
justifyContent: "space-between" as const,
flexWrap: "wrap" as const,
width: "100%",
},
nonIconContainer: {
marginBottom: 16,
width: 455,
marginTop: 15,
"& button": {
marginRight: 16,
},
},
pickTitle: {
fontWeight: 600,
color: "#393939",
fontSize: 14,
marginBottom: 16,
},
centerElements: {
display: "flex" as const,
flexDirection: "column" as const,
alignItems: "center" as const,
justifyContent: "center" as const,
},
logoButton: {
height: "80px",
},
lambdaNotif: {
backgroundColor: "#fff",
border: "#393939 1px solid",
borderRadius: 5,
width: 101,
height: 91,
display: "flex",
alignItems: "center",
justifyContent: "center",
marginBottom: 16,
cursor: "pointer",
"& img": {
maxWidth: 71,
maxHeight: 71,
},
},
};
export const logsCommon = {
logsSubContainer: {
height: "calc(100vh - 230px)",

View File

@@ -14,7 +14,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, { useState } from "react";
import React, { useState, Fragment } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { IWizardMain } from "./types";
import WizardPage from "./WizardPage";
@@ -33,6 +33,9 @@ const styles = (theme: Theme) =>
minHeight: 450,
padding: "0 30px",
},
wizFromModal: {
position: "relative",
},
wizardSteps: {
minWidth: 180,
marginRight: 10,
@@ -43,7 +46,7 @@ const styles = (theme: Theme) =>
height: "100%",
"& ul": {
padding: "0 15px 0 40px",
marginTop: "0px",
marginTop: 0,
"& li": {
listStyle: "lower-roman",
@@ -51,6 +54,23 @@ const styles = (theme: Theme) =>
},
},
},
modalWizardSteps: {
padding: 5,
borderBottom: "#eaeaea 1px solid",
"& ul": {
padding: 0,
marginTop: 0,
display: "flex",
justifyContent: "space-evenly",
"& li": {
listStyle: "lower-roman",
"&::marker": {
paddingLeft: 15,
},
},
},
},
buttonList: {
backgroundColor: "transparent",
border: "none",
@@ -72,10 +92,28 @@ const styles = (theme: Theme) =>
color: "#393939",
fontWeight: 600,
margin: "15px 12px",
"&.stepsModalTitle": {
textAlign: "center",
width: "100%",
marginTop: 0,
marginBottom: 10,
},
},
stepsMasterContainer: {
position: "sticky",
top: 0,
backgroundColor: "#FFFFFF",
width: "100%",
maxHeight: 90,
},
});
const GenericWizard = ({ classes, wizardSteps }: IWizardMain) => {
const GenericWizard = ({
classes,
wizardSteps,
loadingStep,
forModal,
}: IWizardMain) => {
const [currentStep, setCurrentStep] = useState<number>(0);
const pageChange = (toElement: string | number) => {
@@ -119,38 +157,64 @@ const GenericWizard = ({ classes, wizardSteps }: IWizardMain) => {
return null;
}
const stepsList = () => {
return (
<ul>
{wizardSteps.map((step, index) => {
return (
<li key={`wizard-${index.toString()}`}>
<button
onClick={() => pageChange(index)}
disabled={index > currentStep}
className={classes.buttonList}
>
{step.label}
</button>
</li>
);
})}
</ul>
);
};
return (
<Grid container className={classes.wizFromContainer}>
<Grid item xs={12} sm={3} md={3} lg={3} xl={2}>
<div className={classes.wizardSteps}>
<span className={classes.stepsLabel}>Steps</span>
<ul>
{wizardSteps.map((step, index) => {
return (
<li key={`wizard-${index.toString()}`}>
<button
onClick={() => pageChange(index)}
disabled={index > currentStep}
className={classes.buttonList}
>
{step.label}
</button>
</li>
);
})}
</ul>
</div>
</Grid>
<Grid
container
className={forModal ? classes.wizFromModal : classes.wizFromContainer}
>
{forModal ? (
<Fragment>
<div className={classes.stepsMasterContainer}>
<div className={`${classes.stepsLabel} stepsModalTitle`}>Steps</div>
<div className={classes.modalWizardSteps}>{stepsList()}</div>
</div>
</Fragment>
) : (
<Fragment>
<Grid item xs={12} sm={3} md={3} lg={3} xl={2}>
<div className={classes.wizardSteps}>
<span className={classes.stepsLabel}>Steps</span>
{stepsList()}
</div>
</Grid>
</Fragment>
)}
<Grid
item
xs={12}
sm={9}
md={9}
lg={9}
xl={10}
className={classes.paddedContentGrid}
sm={forModal ? 12 : 9}
md={forModal ? 12 : 9}
lg={forModal ? 12 : 9}
xl={forModal ? 12 : 10}
className={forModal ? "" : classes.paddedContentGrid}
>
<WizardPage page={wizardSteps[currentStep]} pageChange={pageChange} />
<WizardPage
page={wizardSteps[currentStep]}
pageChange={pageChange}
loadingStep={loadingStep}
forModal={forModal}
/>
</Grid>
</Grid>
);

View File

@@ -17,7 +17,7 @@
import React from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { IWizardButton, IWizardPage } from "./types";
import { Button } from "@material-ui/core";
import { Button, LinearProgress } from "@material-ui/core";
const styles = (theme: Theme) =>
createStyles({
@@ -29,20 +29,41 @@ const styles = (theme: Theme) =>
overflowY: "auto",
marginBottom: 10,
height: "calc(100vh - 435px)",
maxWidth: 840,
width: "100%",
},
wizardModal: {
overflowY: "auto",
marginBottom: 10,
height: "calc(100vh - 515px)",
},
buttonsContainer: {
display: "flex",
flexDirection: "row",
justifyContent: "flex-end" as const,
justifyContent: "flex-start" as const,
padding: "10px 0",
borderTop: "#EAEAEA 1px solid",
"& button": {
marginLeft: 10,
},
"&.forModal": {
paddingBottom: 0,
},
},
buttonInnerContainer: {
maxWidth: 840,
width: "100%",
textAlign: "right" as const,
},
});
const WizardPage = ({ classes, page, pageChange }: IWizardPage) => {
const WizardPage = ({
classes,
page,
pageChange,
loadingStep,
forModal,
}: IWizardPage) => {
const buttonAction = (btn: IWizardButton) => {
switch (btn.type) {
case "next":
@@ -54,34 +75,46 @@ const WizardPage = ({ classes, page, pageChange }: IWizardPage) => {
case "to":
pageChange(btn.toPage || 0);
break;
case "custom":
default:
}
if (btn.action) {
btn.action();
btn.action(pageChange);
}
};
return (
<div className={classes.wizardStepContainer}>
<div className={classes.wizardComponent}>{page.componentRender}</div>
<div className={classes.buttonsContainer}>
{page.buttons.map((btn) => {
return (
<Button
variant="contained"
color="primary"
size="small"
onClick={() => {
buttonAction(btn);
}}
disabled={!btn.enabled}
key={`button-${page.label}-${btn.label}`}
>
{btn.label}
</Button>
);
})}
<div className={forModal ? classes.wizardModal : classes.wizardComponent}>
{page.componentRender}
</div>
{loadingStep && (
<div>
<LinearProgress />
</div>
)}
<div
className={`${classes.buttonsContainer} ${forModal ? "forModal" : ""}`}
>
<div className={classes.buttonInnerContainer}>
{page.buttons.map((btn) => {
return (
<Button
variant="contained"
color="primary"
size="small"
onClick={() => {
buttonAction(btn);
}}
disabled={!btn.enabled}
key={`button-${page.label}-${btn.label}`}
>
{btn.label}
</Button>
);
})}
</div>
</div>
</div>
);

View File

@@ -17,7 +17,7 @@
export interface IWizardButton {
label: string;
type: string;
action?: () => void;
action?: (nextFunction: (to: string | number) => void) => void;
enabled?: boolean;
toPage?: number;
}
@@ -27,15 +27,20 @@ export interface IWizardElement {
componentRender: any;
buttons: IWizardButton[];
advancedOnly?: boolean;
loadingStep?: boolean;
}
export interface IWizardMain {
classes: any;
loadingStep?: boolean;
wizardSteps: IWizardElement[];
forModal?: boolean;
}
export interface IWizardPage {
classes: any;
page: IWizardElement;
pageChange: (to: string | number) => void;
loadingStep?: boolean;
forModal?: boolean;
}

View File

@@ -32,6 +32,7 @@ interface IModalProps {
children: any;
wideLimit?: boolean;
modalSnackMessage?: snackBarMessage;
noContentPadding?: boolean;
setModalSnackMessage: typeof setModalSnackMessage;
}
@@ -114,6 +115,7 @@ const ModalWrapper = ({
classes,
wideLimit = true,
modalSnackMessage,
noContentPadding,
setModalSnackMessage,
}: IModalProps) => {
const [openSnackbar, setOpenSnackbar] = useState<boolean>(false);
@@ -184,7 +186,7 @@ const ModalWrapper = ({
<DialogTitle id="alert-dialog-title" className={classes.titleClass}>
{title}
</DialogTitle>
<DialogContent className={classes.modalContent}>
<DialogContent className={noContentPadding ? "" : classes.modalContent}>
{children}
</DialogContent>
</div>

View File

@@ -24,6 +24,7 @@ import DescriptionIcon from "./TableActionIcons/DescriptionIcon";
import CloudIcon from "./TableActionIcons/CloudIcon";
import ConsoleIcon from "./TableActionIcons/ConsoleIcon";
import DownloadIcon from "./TableActionIcons/DownloadIcon";
import DisableIcon from "./TableActionIcons/DisableIcon";
import { Link } from "react-router-dom";
import { createStyles, withStyles } from "@material-ui/core/styles";
@@ -32,6 +33,13 @@ const styles = () =>
spacing: {
margin: "0 8px",
},
buttonDisabled: {
"&.MuiButtonBase-root.Mui-disabled": {
cursor: "not-allowed",
filter: "grayscale(100%)",
opacity: "30%",
},
},
});
interface IActionButton {
@@ -42,6 +50,7 @@ interface IActionButton {
selected: boolean;
sendOnlyId?: boolean;
idField: string;
disabled: boolean;
classes: any;
}
@@ -63,6 +72,8 @@ const defineIcon = (type: string, selected: boolean) => {
return <ConsoleIcon active={selected} />;
case "download":
return <DownloadIcon active={selected} />;
case "disable":
return <DisableIcon active={selected} />;
}
return null;
@@ -76,6 +87,7 @@ const TableActionButton = ({
selected,
to,
sendOnlyId = false,
disabled = false,
classes,
}: IActionButton) => {
const valueClick = sendOnlyId ? valueToSend[idField] : valueToSend;
@@ -88,11 +100,16 @@ const TableActionButton = ({
onClick
? (e) => {
e.stopPropagation();
onClick(valueClick);
if (!disabled) {
onClick(valueClick);
} else {
e.preventDefault();
}
}
: () => null
}
className={classes.spacing}
className={`${classes.spacing} ${disabled ? classes.buttonDisabled : ""}`}
disabled={disabled}
>
{defineIcon(type, selected)}
</IconButton>
@@ -103,16 +120,20 @@ const TableActionButton = ({
}
if (isString(to)) {
return (
<Link
to={`${to}/${valueClick}`}
onClick={(e) => {
e.stopPropagation();
}}
>
{buttonElement}
</Link>
);
if (!disabled) {
return (
<Link
to={`${to}/${valueClick}`}
onClick={(e) => {
e.stopPropagation();
}}
>
{buttonElement}
</Link>
);
}
return buttonElement;
}
return null;

View File

@@ -0,0 +1,20 @@
import React from "react";
import { IIcon, selected, unSelected } from "./common";
const DescriptionIcon = ({ active = false }: IIcon) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
>
<path
fill={active ? selected : unSelected}
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11H7v-2h10v2z"
></path>
</svg>
);
};
export default DescriptionIcon;

View File

@@ -45,7 +45,7 @@ interface ItemActions {
type: string;
to?: string;
sendOnlyId?: boolean;
hideButtonFunction?: (itemValue: any) => boolean;
disableButtonFunction?: (itemValue: any) => boolean;
showLoaderFunction?: (itemValue: any) => boolean;
onClick?(valueToSend: any): any;
@@ -434,9 +434,11 @@ const elementActions = (
const vlSend =
typeof valueToSend === "string" ? valueToSend : valueToSend[idField];
if (action.hideButtonFunction) {
if (action.hideButtonFunction(vlSend)) {
return null;
let disabled = false;
if (action.disableButtonFunction) {
if (action.disableButtonFunction(vlSend)) {
disabled = true;
}
}
@@ -465,6 +467,7 @@ const elementActions = (
key={`actions-${action.type}-${index.toString()}`}
idField={idField}
sendOnlyId={!!action.sendOnlyId}
disabled={disabled}
/>
);
});

View File

@@ -1,3 +1,19 @@
// 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, useState } from "react";
import PageHeader from "../Common/PageHeader/PageHeader";
import { Grid } from "@material-ui/core";
@@ -7,9 +23,14 @@ import Tab from "@material-ui/core/Tab";
import Tabs from "@material-ui/core/Tabs";
import ConfigurationsList from "./ConfigurationPanels/ConfigurationsList";
import ListNotificationEndpoints from "./NotificationEndpoints/ListNotificationEndpoints";
import ListTiersConfiguration from "./TiersConfiguration/ListTiersConfiguration";
import { AppState } from "../../../store";
import { connect } from "react-redux";
import { ISessionResponse } from "../types";
interface IConfigurationMain {
classes: any;
session: ISessionResponse;
}
const styles = (theme: Theme) =>
@@ -23,8 +44,10 @@ const styles = (theme: Theme) =>
...containerForHeader(theme.spacing(4)),
});
const ConfigurationMain = ({ classes }: IConfigurationMain) => {
const ConfigurationMain = ({ classes, session }: IConfigurationMain) => {
const [selectedTab, setSelectedTab] = useState<number>(0);
const ilmEnabled = session.features?.indexOf("ilm") > -1;
return (
<Fragment>
<PageHeader label="Settings" />
@@ -41,9 +64,12 @@ const ConfigurationMain = ({ classes }: IConfigurationMain) => {
setSelectedTab(newValue);
}}
aria-label="tenant-tabs"
variant="scrollable"
scrollButtons="auto"
>
<Tab label="Configurations" />
<Tab label="Lambda Notifications" />
{ilmEnabled && <Tab label="Tiers" />}
</Tabs>
<Grid item xs={12}>
{selectedTab === 0 && (
@@ -56,6 +82,11 @@ const ConfigurationMain = ({ classes }: IConfigurationMain) => {
<ListNotificationEndpoints />
</Grid>
)}
{selectedTab === 2 && (
<Grid item xs={12}>
<ListTiersConfiguration />
</Grid>
)}
</Grid>
</Grid>
</Grid>
@@ -63,4 +94,10 @@ const ConfigurationMain = ({ classes }: IConfigurationMain) => {
);
};
export default withStyles(styles)(ConfigurationMain);
const mapState = (state: AppState) => ({
session: state.console.session,
});
const connector = connect(mapState, {});
export default withStyles(styles)(connector(ConfigurationMain));

View File

@@ -158,14 +158,16 @@ const EditConfiguration = ({
/>
</Grid>
<Grid item xs={12} className={classes.settingsButtonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={saving}
>
Save
</Button>
<Grid item xs={12} className={classes.innerSettingsButtonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={saving}
>
Save
</Button>
</Grid>
</Grid>
</form>
</Fragment>

View File

@@ -161,14 +161,20 @@ const AddNotificationEndpoint = ({
{srvComponent}
</Grid>
<Grid item xs={12} className={classes.settingsButtonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={saving}
<Grid
item
xs={12}
className={classes.innerSettingsButtonContainer}
>
Save
</Button>
<Button
type="submit"
variant="contained"
color="primary"
disabled={saving}
>
Save
</Button>
</Grid>
</Grid>
<Grid item xs={9} />
</form>

View File

@@ -19,7 +19,10 @@ import Grid from "@material-ui/core/Grid";
import { Button } from "@material-ui/core";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { servicesList } from "./utils";
import { settingsCommon } from "../../Common/FormComponents/common/styleLibrary";
import {
settingsCommon,
typesSelection,
} from "../../Common/FormComponents/common/styleLibrary";
interface INotificationTypeSelector {
classes: any;
@@ -32,56 +35,11 @@ const withLogos = servicesList.filter((elService) => elService.logo !== "");
const styles = (theme: Theme) =>
createStyles({
...settingsCommon,
logoButton: {
height: "80px",
},
lambdaNotif: {
backgroundColor: "#fff",
border: "#393939 1px solid",
borderRadius: 5,
width: 101,
height: 91,
display: "flex",
alignItems: "center",
justifyContent: "center",
marginBottom: 16,
cursor: "pointer",
"& img": {
maxWidth: 71,
maxHeight: 71,
},
},
iconContainer: {
display: "flex",
flexDirection: "row",
maxWidth: 455,
justifyContent: "space-between",
flexWrap: "wrap",
},
nonIconContainer: {
marginBottom: 16,
width: 455,
marginTop: 15,
"& button": {
marginRight: 16,
},
},
pickTitle: {
fontWeight: 600,
color: "#393939",
fontSize: 14,
marginBottom: 16,
},
centerElements: {
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
},
customTitle: {
...settingsCommon.customTitle,
marginTop: 0,
},
...typesSelection,
});
const NotificationTypeSelector = ({

View File

@@ -0,0 +1,431 @@
// 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, useEffect, useState } from "react";
import { connect } from "react-redux";
import Grid from "@material-ui/core/Grid";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Button } from "@material-ui/core";
import api from "../../../../common/api";
import { setErrorSnackMessage } from "../../../../actions";
import {
modalBasic,
settingsCommon,
} from "../../Common/FormComponents/common/styleLibrary";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import FileSelector from "../../Common/FormComponents/FileSelector/FileSelector";
const styles = (theme: Theme) =>
createStyles({
...modalBasic,
...settingsCommon,
strongText: {
fontWeight: 700,
},
keyName: {
marginLeft: 5,
},
buttonContainer: {
textAlign: "right",
},
customTitle: {
...settingsCommon.customTitle,
marginTop: 0,
},
settingsFormContainer: {
...settingsCommon.settingsFormContainer,
height: "calc(100vh - 422px)",
},
});
interface IAddNotificationEndpointProps {
saveAndRefresh: any;
setErrorSnackMessage: typeof setErrorSnackMessage;
classes: any;
type: string;
}
const AddTierConfiguration = ({
saveAndRefresh,
classes,
setErrorSnackMessage,
type,
}: IAddNotificationEndpointProps) => {
//Local States
const [saving, setSaving] = useState<boolean>(false);
// Form Items
const [name, setName] = useState<string>("");
const [endpoint, setEndpoint] = useState<string>("");
const [bucket, setBucket] = useState<string>("");
const [prefix, setPrefix] = useState<string>("");
const [region, setRegion] = useState<string>("");
const [storageClass, setStorageClass] = useState<string>("");
const [accessKey, setAccessKey] = useState<string>("");
const [secretKey, setSecretKey] = useState<string>("");
const [creds, setCreds] = useState<string>("");
const [encodedCreds, setEncodedCreds] = useState<string>("");
const [accountName, setAccountName] = useState<string>("");
const [accountKey, setAccountKey] = useState<string>("");
const [titleSelection, setTitleSelection] = useState<string>("");
// Validations
const [isFormValid, setIsFormValid] = useState<boolean>(true);
//Effects
useEffect(() => {
if (saving) {
let request = {};
let fields = {
name,
endpoint,
bucket,
prefix,
region,
};
let tierType = type;
if (type === "minio") {
tierType = "s3";
}
switch (type) {
case "minio":
case "s3":
request = {
s3: {
...fields,
accesskey: accessKey,
secretkey: secretKey,
storageclass: storageClass,
},
};
break;
case "gcs":
request = {
gcs: {
...fields,
creds: encodedCreds,
},
};
break;
case "azure":
request = {
azure: {
...fields,
accountname: accountName,
accountkey: accountKey,
},
};
}
let payload = {
type: tierType,
...request,
};
api
.invoke("POST", `/api/v1/admin/tiers`, payload)
.then(() => {
setSaving(false);
saveAndRefresh();
})
.catch((err) => {
setSaving(false);
setErrorSnackMessage(err);
});
}
}, [
accessKey,
accountKey,
accountName,
bucket,
encodedCreds,
endpoint,
name,
prefix,
region,
saveAndRefresh,
saving,
secretKey,
setErrorSnackMessage,
storageClass,
type,
]);
useEffect(() => {
let valid = true;
if (type === "") {
valid = false;
}
if (type === "") {
valid = false;
}
if (name === "") {
valid = false;
}
if (endpoint === "") {
valid = false;
}
if (bucket === "") {
valid = false;
}
if (prefix === "") {
valid = false;
}
if (region === "") {
valid = false;
}
if (type === "s3" || type === "minio") {
if (accessKey === "") {
valid = false;
}
if (secretKey === "") {
valid = false;
}
}
if (type === "gcs") {
if (encodedCreds === "") {
valid = false;
}
}
if (type === "azure") {
if (accountName === "") {
valid = false;
}
if (accountKey === "") {
valid = false;
}
}
setIsFormValid(valid);
}, [
accessKey,
accountKey,
accountName,
bucket,
encodedCreds,
endpoint,
isFormValid,
name,
prefix,
region,
secretKey,
storageClass,
type,
]);
useEffect(() => {
switch (type) {
case "gcs":
setEndpoint("https://storage.googleapis.com/");
setTitleSelection("Google Cloud");
break;
case "s3":
setEndpoint("https://s3.amazonaws.com");
setTitleSelection("Amazon S3");
break;
case "azure":
setEndpoint("http://blob.core.windows.net");
setTitleSelection("Azure");
break;
case "minio":
setEndpoint("");
setTitleSelection("MinIO");
}
}, [type]);
//Fetch Actions
const submitForm = (event: React.FormEvent) => {
event.preventDefault();
setSaving(true);
};
return (
<Fragment>
<form noValidate onSubmit={submitForm}>
<Grid item xs={12} className={classes.customTitle}>
{titleSelection} - Add Tier Configuration
</Grid>
<Grid item xs={12} className={classes.settingsFormContainer}>
<Grid container>
{type !== "" && (
<Fragment>
<InputBoxWrapper
id="name"
name="name"
label="Name"
placeholder="Enter Name"
value={name}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setName(e.target.value);
}}
/>
<InputBoxWrapper
id="endpoint"
name="endpoint"
label="Endpoint"
placeholder="Enter Endpoint"
value={endpoint}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setEndpoint(e.target.value);
}}
/>
{type === "s3" ||
(type === "minio" && (
<Fragment>
<InputBoxWrapper
id="accessKey"
name="accessKey"
label="Access Key"
placeholder="Enter Access Key"
value={accessKey}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAccessKey(e.target.value);
}}
/>
<InputBoxWrapper
id="secretKey"
name="secretKey"
label="Secret Key"
placeholder="Enter Secret Key"
value={secretKey}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setSecretKey(e.target.value);
}}
/>
</Fragment>
))}
{type === "gcs" && (
<Fragment>
<FileSelector
accept=".json"
id="creds"
label="Credentials"
name="creds"
onChange={(encodedValue, fileName) => {
setEncodedCreds(encodedValue);
setCreds(fileName);
}}
value={creds}
/>
</Fragment>
)}
{type === "azure" && (
<Fragment>
<InputBoxWrapper
id="accountName"
name="accountName"
label="Account Name"
placeholder="Enter Account Name"
value={accountName}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAccountName(e.target.value);
}}
/>
<InputBoxWrapper
id="accountKey"
name="accountKey"
label="Account Key"
placeholder="Enter Account Key"
value={accountKey}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAccountKey(e.target.value);
}}
/>
</Fragment>
)}
<InputBoxWrapper
id="bucket"
name="bucket"
label="Bucket"
placeholder="Enter Bucket"
value={bucket}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setBucket(e.target.value);
}}
/>
<InputBoxWrapper
id="prefix"
name="prefix"
label="Prefix"
placeholder="Enter Prefix"
value={prefix}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setPrefix(e.target.value);
}}
/>
<InputBoxWrapper
id="region"
name="region"
label="Region"
placeholder="Enter Region"
value={region}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setRegion(e.target.value);
}}
/>
{type === "s3" ||
(type === "minio" && (
<InputBoxWrapper
id="storageClass"
name="storageClass"
label="Storage Class"
placeholder="Enter Storage Class"
value={storageClass}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setStorageClass(e.target.value);
}}
/>
))}
</Fragment>
)}
</Grid>
</Grid>
<Grid item xs={12} className={classes.settingsButtonContainer}>
<Grid item xs={12} className={classes.innerSettingsButtonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={saving || !isFormValid}
>
Save
</Button>
</Grid>
</Grid>
</form>
</Fragment>
);
};
const mapDispatchToProps = {
setErrorSnackMessage,
};
const connector = connect(null, mapDispatchToProps);
export default withStyles(styles)(connector(AddTierConfiguration));

View File

@@ -0,0 +1,421 @@
// 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, useState, Fragment } from "react";
import get from "lodash/get";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { TextField } from "@material-ui/core";
import Grid from "@material-ui/core/Grid";
import Button from "@material-ui/core/Button";
import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search";
import {
actionsTray,
containerForHeader,
searchField,
settingsCommon,
typesSelection,
} from "../../Common/FormComponents/common/styleLibrary";
import { CreateIcon } from "../../../../icons";
import { setErrorSnackMessage } from "../../../../actions";
import { ITierElement, ITierResponse } from "./types";
import api from "../../../../common/api";
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
import SlideOptions from "../../Common/SlideOptions/SlideOptions";
import BackSettingsIcon from "../../../../icons/BackSettingsIcon";
import AddTierConfiguration from "./AddTierConfiguration";
import UpdateTierCredentiasModal from "./UpdateTierCredentiasModal";
interface IListTiersConfig {
classes: any;
setErrorSnackMessage: typeof setErrorSnackMessage;
}
const styles = (theme: Theme) =>
createStyles({
...actionsTray,
...searchField,
...settingsCommon,
...typesSelection,
...containerForHeader(theme.spacing(4)),
strongText: {
fontWeight: 700,
},
keyName: {
marginLeft: 5,
},
iconText: {
lineHeight: "24px",
},
customConfigurationPage: {
height: "calc(100vh - 410px)",
scrollbarWidth: "none" as const,
"&::-webkit-scrollbar": {
display: "none",
},
},
lambdaContainer: {
padding: "15px 0",
},
actionsTray: {
...actionsTray.actionsTray,
padding: "0 38px",
},
customTitle: {
...settingsCommon.customTitle,
marginTop: 0,
},
});
const ListTiersConfiguration = ({
classes,
setErrorSnackMessage,
}: IListTiersConfig) => {
const [records, setRecords] = useState<ITierElement[]>([]);
const [filter, setFilter] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(true);
const [currentPanel, setCurrentPanel] = useState<number>(0);
const [updateCredentialsOpen, setUpdateCredentialsOpen] = useState<boolean>(
false
);
const [selectedTier, setSelectedTier] = useState<ITierElement>({
type: "unsupported",
});
const [type, setType] = useState<string>("");
useEffect(() => {
if (isLoading) {
const fetchRecords = () => {
api
.invoke("GET", `/api/v1/admin/tiers`)
.then((res: ITierResponse) => {
setRecords(res.items || []);
setIsLoading(false);
})
.catch((err) => {
setErrorSnackMessage(err);
setIsLoading(false);
});
};
fetchRecords();
}
}, [isLoading, setErrorSnackMessage]);
const filteredRecords = records.filter((b: ITierElement) => {
if (filter === "") {
return true;
}
const getItemName = get(b, `${b.type}.name`, "");
const getItemType = get(b, `type`, "");
return getItemName.indexOf(filter) >= 0 || getItemType.indexOf(filter) >= 0;
});
const backClick = () => {
setCurrentPanel(currentPanel - 1);
};
const addTier = () => {
setCurrentPanel(1);
};
const tierAdded = () => {
setCurrentPanel(0);
setIsLoading(true);
};
const renderTierName = (item: ITierElement) => {
const name = get(item, `${item.type}.name`, "");
if (name !== null) {
return name;
}
return "";
};
const renderTierPrefix = (item: ITierElement) => {
const prefix = get(item, `${item.type}.prefix`, "");
if (prefix !== null) {
return prefix;
}
return "";
};
const renderTierEndpoint = (item: ITierElement) => {
const endpoint = get(item, `${item.type}.endpoint`, "");
if (endpoint !== null) {
return endpoint;
}
return "";
};
const renderTierBucket = (item: ITierElement) => {
const bucket = get(item, `${item.type}.bucket`, "");
if (bucket !== null) {
return bucket;
}
return "";
};
const renderTierRegion = (item: ITierElement) => {
const region = get(item, `${item.type}.region`, "");
if (region !== null) {
return region;
}
return "";
};
const closeTierCredentials = () => {
setUpdateCredentialsOpen(false);
};
const typeSelect = (typeItem: string) => {
setType(typeItem);
setCurrentPanel(2);
};
return (
<Fragment>
{updateCredentialsOpen && (
<UpdateTierCredentiasModal
open={updateCredentialsOpen}
tierData={selectedTier}
closeModalAndRefresh={closeTierCredentials}
/>
)}
<Grid container>
<Grid item xs={12}>
<Grid item xs={12}>
<div className={classes.settingsOptionsContainer}>
<SlideOptions
slideOptions={[
<Fragment>
<Grid item xs={12} className={classes.customTitle}>
Tiers
</Grid>
<Grid item xs={12} className={classes.lambdaContainer}>
<Grid item xs={12} className={classes.actionsTray}>
<TextField
placeholder="Filter"
className={classes.searchField}
id="search-resource"
label=""
onChange={(event) => {
setFilter(event.target.value);
}}
InputProps={{
disableUnderline: true,
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
/>
<Button
variant="contained"
color="primary"
startIcon={<CreateIcon />}
onClick={addTier}
>
Add Tier
</Button>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<TableWrapper
itemActions={[
{
type: "edit",
onClick: (tierData: ITierElement) => {
setSelectedTier(tierData);
setUpdateCredentialsOpen(true);
},
},
]}
columns={[
{
label: "Tier Name",
elementKey: "type",
renderFunction: renderTierName,
renderFullObject: true,
},
{
label: "Type",
elementKey: "type",
width: 150,
},
{
label: "Endpoint",
elementKey: "type",
renderFunction: renderTierEndpoint,
renderFullObject: true,
},
{
label: "Bucket",
elementKey: "type",
renderFunction: renderTierBucket,
renderFullObject: true,
},
{
label: "Prefix",
elementKey: "type",
renderFunction: renderTierPrefix,
renderFullObject: true,
},
{
label: "Region",
elementKey: "type",
renderFunction: renderTierRegion,
renderFullObject: true,
},
]}
isLoading={isLoading}
records={filteredRecords}
entityName="Tiers"
idField="service_name"
customPaperHeight={classes.customConfigurationPage}
noBackground
/>
</Grid>
</Grid>
</Fragment>,
<Fragment>
<Grid item xs={12} className={classes.backContainer}>
<button
onClick={backClick}
className={classes.backButton}
>
<BackSettingsIcon />
Back To Tiers
</button>
</Grid>
<Grid item xs={12}>
<Grid item xs={12} className={classes.customTitle}>
Add Tier Configuration
</Grid>
<Grid
item
xs={12}
className={classes.settingsFormContainer}
>
<Grid item xs={12}>
<Grid item xs={12} className={classes.centerElements}>
<div className={classes.iconContainer}>
<button
className={classes.lambdaNotif}
onClick={() => {
typeSelect("minio");
}}
>
<img
src={"/minioTier.png"}
className={classes.logoButton}
alt={"MinIO"}
/>
</button>
<button
className={classes.lambdaNotif}
onClick={() => {
typeSelect("gcs");
}}
>
<img
src={"/gcs.png"}
className={classes.logoButton}
alt={"GCS"}
/>
</button>
<button
className={classes.lambdaNotif}
onClick={() => {
typeSelect("s3");
}}
>
<img
src={"/amazon.png"}
className={classes.logoButton}
alt={"s3"}
/>
</button>
<button
className={classes.lambdaNotif}
onClick={() => {
typeSelect("azure");
}}
>
<img
src={"/azure.png"}
className={classes.logoButton}
alt={"Azure"}
/>
</button>
</div>
</Grid>
</Grid>
</Grid>
</Grid>
</Fragment>,
<Fragment>
<Grid item xs={12} className={classes.backContainer}>
<button
onClick={backClick}
className={classes.backButton}
>
<BackSettingsIcon />
Back To Tier Type Selection
</button>
</Grid>
<Grid item xs={12}>
{currentPanel === 2 && (
<AddTierConfiguration
type={type}
saveAndRefresh={tierAdded}
/>
)}
</Grid>
</Fragment>,
]}
currentSlide={currentPanel}
/>
</div>
</Grid>
</Grid>
</Grid>
</Fragment>
);
};
const mapDispatchToProps = {
setErrorSnackMessage,
};
const connector = connect(null, mapDispatchToProps);
export default withStyles(styles)(connector(ListTiersConfiguration));

View File

@@ -0,0 +1,232 @@
// 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, { useState, useEffect, Fragment } from "react";
import get from "lodash/get";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Button, LinearProgress } from "@material-ui/core";
import Grid from "@material-ui/core/Grid";
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
import { setModalErrorSnackMessage } from "../../../../actions";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import FileSelector from "../../Common/FormComponents/FileSelector/FileSelector";
import api from "../../../../common/api";
import { ITierElement } from "./types";
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
interface ITierCredentialsModal {
open: boolean;
closeModalAndRefresh: (refresh: boolean) => any;
classes: any;
tierData: ITierElement;
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
}
const styles = (theme: Theme) =>
createStyles({
minTableHeader: {
color: "#393939",
"& tr": {
"& th": {
fontWeight: "bold",
},
},
},
buttonContainer: {
textAlign: "right",
},
...modalBasic,
});
const UpdateTierCredentialsModal = ({
open,
closeModalAndRefresh,
classes,
tierData,
setModalErrorSnackMessage,
}: ITierCredentialsModal) => {
const [savingTiers, setSavingTiers] = useState<boolean>(false);
const [accessKey, setAccessKey] = useState<string>("");
const [secretKey, setSecretKey] = useState<string>("");
const [creds, setCreds] = useState<string>("");
const [encodedCreds, setEncodedCreds] = useState<string>("");
const [accountName, setAccountName] = useState<string>("");
const [accountKey, setAccountKey] = useState<string>("");
// Validations
const [isFormValid, setIsFormValid] = useState<boolean>(true);
const type = get(tierData, "type", "");
const name = get(tierData, `${type}.name`, "");
useEffect(() => {
let valid = true;
if (type === "s3" || type === "azure") {
if (accountName === "" || accountKey === "") {
valid = false;
}
} else if (type === "gcs") {
if (encodedCreds === "") {
valid = false;
}
}
setIsFormValid(valid);
}, [accountKey, accountName, encodedCreds, type]);
const addRecord = () => {
let rules = {};
if (type === "s3" || type === "azure") {
rules = {
access_key: accountName,
secret_key: accountKey,
};
} else if (type === "gcs") {
rules = {
creds: encodedCreds,
};
}
if (name !== "") {
api
.invoke("PUT", `/api/v1/admin/tiers/${type}/${name}/credentials`, rules)
.then(() => {
setSavingTiers(false);
closeModalAndRefresh(true);
})
.catch((err) => {
setSavingTiers(false);
setModalErrorSnackMessage(err);
});
} else {
setModalErrorSnackMessage(
"There was an error retrieving tier information"
);
}
};
return (
<ModalWrapper
modalOpen={open}
onClose={() => {
closeModalAndRefresh(false);
}}
title={`Update Credentials - ${type} / ${name}`}
>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setSavingTiers(true);
addRecord();
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
{type === "s3" && (
<Fragment>
<InputBoxWrapper
id="accessKey"
name="accessKey"
label="Access Key"
placeholder="Enter Access Key"
value={accessKey}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAccessKey(e.target.value);
}}
/>
<InputBoxWrapper
id="secretKey"
name="secretKey"
label="Secret Key"
placeholder="Enter Secret Key"
value={secretKey}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setSecretKey(e.target.value);
}}
/>
</Fragment>
)}
{type === "gcs" && (
<Fragment>
<FileSelector
accept=".json"
id="creds"
label="Credentials"
name="creds"
onChange={(encodedValue, fileName) => {
setEncodedCreds(encodedValue);
setCreds(fileName);
}}
value={creds}
/>
</Fragment>
)}
{type === "azure" && (
<Fragment>
<InputBoxWrapper
id="accountName"
name="accountName"
label="Account Name"
placeholder="Enter Account Name"
value={accountName}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAccountName(e.target.value);
}}
/>
<InputBoxWrapper
id="accountKey"
name="accountKey"
label="Account Key"
placeholder="Enter Account Key"
value={accountKey}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAccountKey(e.target.value);
}}
/>
</Fragment>
)}
</Grid>
<Grid item xs={12} className={classes.buttonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={savingTiers || !isFormValid}
>
Save
</Button>
</Grid>
{savingTiers && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</Grid>
</form>
</ModalWrapper>
);
};
const connector = connect(null, {
setModalErrorSnackMessage,
});
export default withStyles(styles)(connector(UpdateTierCredentialsModal));

View File

@@ -0,0 +1,64 @@
// 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/>.
export interface ITierS3 {
name: string;
endpoint: string;
accesskey: string;
secretkey: string;
bucket: string;
prefix: string;
region: string;
storageclass: string;
}
export interface ITierGCS {
name: string;
endpoint: string;
creds: string;
bucket: string;
prefix: string;
region: string;
storageclass: string;
}
export interface ITierAzure {
name: string;
endpoint: string;
accountname: string;
accountkey: string;
bucket: string;
prefix: string;
region: string;
storageclass: string;
}
export interface ITierElement {
type: "s3" | "gcs" | "azure" | "unsupported";
s3?: ITierS3;
gcs?: ITierGCS;
azure?: ITierAzure;
}
export interface ITierResponse {
items: ITierElement[];
}
export interface ITierUpdateCreds {
access_key: string;
secret_key: string;
creds: string;
}

View File

@@ -477,6 +477,7 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
<img
className={classes.verifiedIcon}
src={"/verified.svg"}
alt="verified"
/>
</Grid>
</React.Fragment>
@@ -526,7 +527,9 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
className={classes.button}
target="_blank"
rel="noopener noreferrer"
href="https://subnet.min.io/support/?ref=op"
href={`https://subnet.min.io/support/?ref=${
operatorMode ? "op" : "con"
}`}
>
Login to SUBNET
</Button>
@@ -572,7 +575,9 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
</Typography>
<br />
<a
href="https://min.io/compliance?ref=op"
href={`https://min.io/compliance?ref=${
operatorMode ? "op" : "con"
}`}
className={classes.openSourcePolicy}
target="_blank"
rel="nofollow noopener noreferrer"
@@ -582,7 +587,9 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
<br />
<br />
<a
href="https://min.io/logo?ref=op"
href={`https://min.io/logo?ref=${
operatorMode ? "op" : "con"
}`}
className={classes.openSourcePolicy}
target="_blank"
rel="nofollow noopener noreferrer"
@@ -796,7 +803,12 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
}
onClick={(e) => {
e.preventDefault();
window.open(button.link, "_blank");
window.open(
`${button.link}/?ref=${
operatorMode ? "op" : "con"
}`,
"_blank"
);
}}
>
{currentPlanID !== index && index > 0

View File

@@ -76,6 +76,8 @@ const LogsMain = ({ classes }: ILogsMainProps) => {
indicatorColor="primary"
textColor="primary"
aria-label="cluster-tabs"
variant="scrollable"
scrollButtons="auto"
>
<Tab label="Error Logs" />
{showLogSearch && <Tab label="Logs Search" />}

View File

@@ -32,7 +32,7 @@ import { AppState } from "../../../store";
import { userLoggedIn } from "../../../actions";
import api from "../../../common/api";
import { menuGroups } from "./utils";
import { IMenuProps } from "./types";
import { IMenuProps, IMenuItem } from "./types";
import {
BucketsIcon,
ClustersIcon,
@@ -46,6 +46,7 @@ import {
UsersIcon,
WarpIcon,
} from "../../../icons";
import LibraryBooksIcon from "@material-ui/icons/LibraryBooks";
import { clearSession } from "../../../common/utils";
import LicenseIcon from "../../../icons/LicenseIcon";
import LogoutIcon from "../../../icons/LogoutIcon";
@@ -189,7 +190,7 @@ const Menu = ({ userLoggedIn, classes, pages, operatorMode }: IMenuProps) => {
});
};
let menuItems = [
let menuItems: IMenuItem[] = [
{
group: "common",
type: "item",
@@ -320,30 +321,76 @@ const Menu = ({ userLoggedIn, classes, pages, operatorMode }: IMenuProps) => {
},
];
const allowedPages = pages.reduce((result: any, item: any, index: any) => {
const allowedPages = pages.reduce((result: any, item: any) => {
result[item] = true;
return result;
}, {});
const documentation: IMenuItem = {
group: "License",
type: "item",
component: NavLink,
to: "/documentation",
name: "Documentation",
icon: <LibraryBooksIcon />,
forceDisplay: true,
};
// Append the license page according to the allowedPages
if (allowedPages.hasOwnProperty("/tenants")) {
menuItems.push({
group: "Operator",
type: "item",
component: NavLink,
to: "/license",
name: "License",
icon: <LicenseIcon />,
});
menuItems.push(
{
group: "Operator",
type: "item",
component: NavLink,
to: "/license",
name: "License",
icon: <LicenseIcon />,
},
{
...documentation,
group: "Operator",
onClick: (
e:
| React.MouseEvent<HTMLLIElement>
| React.MouseEvent<HTMLAnchorElement>
| React.MouseEvent<HTMLDivElement>
) => {
e.preventDefault();
window.open(
`https://docs.min.io/?ref=${operatorMode ? "op" : "con"}`,
"_blank"
);
},
}
);
} else {
menuItems.push({
group: "License",
type: "item",
component: NavLink,
to: "/license",
name: "License",
icon: <LicenseIcon />,
});
menuItems.push(
{
group: "License",
type: "item",
component: NavLink,
to: "/license",
name: "License",
icon: <LicenseIcon />,
},
{
...documentation,
group: "License",
onClick: (
e:
| React.MouseEvent<HTMLLIElement>
| React.MouseEvent<HTMLAnchorElement>
| React.MouseEvent<HTMLDivElement>
) => {
e.preventDefault();
window.open(
`https://docs.min.io/?ref=${operatorMode ? "op" : "con"}`,
"_blank"
);
},
}
);
}
const allowedItems = menuItems.filter(
@@ -409,13 +456,14 @@ const Menu = ({ userLoggedIn, classes, pages, operatorMode }: IMenuProps) => {
unmountOnExit
key={`menuGroup-${groupMember.group}`}
>
{filterByGroup.map((page: any) => {
{filterByGroup.map((page: IMenuItem) => {
switch (page.type) {
case "item": {
return (
<ListItem
key={page.to}
button
onClick={page.onClick}
component={page.component}
to={page.to}
className={

View File

@@ -22,3 +22,15 @@ export interface IMenuProps {
pages: string[];
operatorMode: boolean;
}
export interface IMenuItem {
group: string;
type: string;
component: any;
to: string;
name: string;
icon: any;
onClick?: any;
forceDisplay?: boolean;
extraMargin?: boolean;
}

View File

@@ -135,7 +135,7 @@ const BrowseBuckets = ({
})
.then((res: HasPermissionResponse) => {
const canCreate = res.permissions
.filter((s) => s.id == "createBucket")
.filter((s) => s.id === "createBucket")
.pop();
if (canCreate && canCreate.can) {
setCanCreateBucket(true);
@@ -151,7 +151,7 @@ const BrowseBuckets = ({
setErrorSnackMessage(err);
});
}
}, [loadingPerms, setErrorSnackMessage]);
}, [loadingPerms]);
useEffect(() => {
resetRoutesList(true);

View File

@@ -14,7 +14,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, { useEffect, useState, Fragment } from "react";
import React, { Fragment, useEffect, useState } from "react";
import { connect } from "react-redux";
import Grid from "@material-ui/core/Grid";
import { LinearProgress } from "@material-ui/core";
@@ -30,11 +30,11 @@ import { generatePoolName } from "../../../../common/utils";
import GenericWizard from "../../Common/GenericWizard/GenericWizard";
import { IWizardElement } from "../../Common/GenericWizard/types";
import { NewServiceAccount } from "../../Common/CredentialsPrompt/types";
import { IAffinityModel, ITenantCreator } from "../../../../common/types";
import { ITenantCreator } from "../../../../common/types";
import { KeyPair } from "../ListTenants/utils";
import { setModalErrorSnackMessage } from "../../../../actions";
import { getHardcodedAffinity } from "../TenantDetails/utils";
import { getDefaultAffinity, getNodeSelector } from "../TenantDetails/utils";
import CredentialsPrompt from "../../Common/CredentialsPrompt/CredentialsPrompt";
import NameTenant from "./Steps/NameTenant";
import { AppState } from "../../../../store";
@@ -46,6 +46,7 @@ import Security from "./Steps/Security";
import Encryption from "./Steps/Encryption";
import TenantSize from "./Steps/TenantSize";
import Preview from "./Steps/Preview";
import Affinity from "./Steps/Affinity";
interface IAddTenantProps {
closeAndRefresh: (reloadData: boolean) => any;
@@ -112,6 +113,8 @@ const AddTenant = ({
const ADGroupBaseDN = fields.identityProvider.ADGroupBaseDN;
const ADGroupSearchFilter = fields.identityProvider.ADGroupSearchFilter;
const ADNameAttribute = fields.identityProvider.ADNameAttribute;
const accessKeys = fields.identityProvider.accessKeys;
const secretKeys = fields.identityProvider.secretKeys;
const minioCertificates = certificates.minioCertificates;
const caCertificates = certificates.caCertificates;
const consoleCertificate = certificates.consoleCertificate;
@@ -152,14 +155,35 @@ const AddTenant = ({
const ecParity = fields.tenantSize.ecParity;
const distribution = fields.tenantSize.distribution;
const memorySize = fields.tenantSize.memorySize;
const logSearchCustom = fields.configure.logSearchCustom;
const prometheusCustom = fields.configure.prometheusCustom;
const logSearchVolumeSize = fields.configure.logSearchVolumeSize;
const logSearchSelectedStorageClass =
fields.configure.logSearchSelectedStorageClass;
const logSearchImage = fields.configure.logSearchImage;
const logSearchPostgresImage = fields.configure.logSearchPostgresImage;
const prometheusImage = fields.configure.prometheusImage;
const prometheusSelectedStorageClass =
fields.configure.prometheusSelectedStorageClass;
const prometheusVolumeSize = fields.configure.prometheusVolumeSize;
const affinityType = fields.affinity.podAffinity;
const affinityLabels = fields.affinity.affinityLabels;
if (addSending) {
const poolName = generatePoolName([]);
const hardCodedAffinity: IAffinityModel = getHardcodedAffinity(
tenantName,
poolName
);
let affinityObject = {};
switch (affinityType) {
case "default":
affinityObject = {
affinity: getDefaultAffinity(tenantName, poolName),
};
break;
case "nodeSelector":
affinityObject = { affinity: getNodeSelector(affinityLabels) };
break;
}
const erasureCode = ecParity.split(":")[1];
@@ -168,6 +192,8 @@ const AddTenant = ({
namespace: namespace,
access_key: "",
secret_key: "",
access_keys: [],
secret_keys: [],
enable_tls: enableTLS && enableAutoCert,
enable_console: true,
enable_prometheus: true,
@@ -193,7 +219,7 @@ const AddTenant = ({
memory: memorySize.limit,
},
},
affinity: hardCodedAffinity,
...affinityObject,
},
],
erasureCodingParity: parseInt(erasureCode, 10),
@@ -210,6 +236,44 @@ const AddTenant = ({
};
}
if (logSearchCustom) {
dataSend = {
...dataSend,
logSearchConfiguration: {
storageClass: logSearchSelectedStorageClass,
storageSize: parseInt(logSearchVolumeSize),
image: logSearchImage,
postgres_image: logSearchPostgresImage,
},
};
} else {
dataSend = {
...dataSend,
logSearchConfiguration: {
image: logSearchImage,
postgres_image: logSearchPostgresImage,
},
};
}
if (prometheusCustom) {
dataSend = {
...dataSend,
prometheusConfiguration: {
storageClass: prometheusSelectedStorageClass,
storageSize: parseInt(prometheusVolumeSize),
image: prometheusImage,
},
};
} else {
dataSend = {
...dataSend,
prometheusConfiguration: {
image: prometheusImage,
},
};
}
let tenantCerts: any = null;
let consoleCerts: any = null;
let caCerts: any = null;
@@ -401,41 +465,50 @@ const AddTenant = ({
};
}
if (idpSelection !== "Built-in") {
let dataIDP: any = {};
switch (idpSelection) {
case "OpenID":
dataIDP = {
oidc: {
url: openIDURL,
client_id: openIDClientID,
secret_id: openIDSecretID,
},
};
break;
case "AD":
dataIDP = {
active_directory: {
url: ADURL,
skip_tls_verification: ADSkipTLS,
server_insecure: ADServerInsecure,
username_format: "",
user_search_filter: ADUserNameFilter,
group_search_base_dn: ADGroupBaseDN,
group_search_filter: ADGroupSearchFilter,
group_name_attribute: ADNameAttribute,
},
};
break;
}
dataSend = {
...dataSend,
idp: { ...dataIDP },
};
let dataIDP: any = {};
switch (idpSelection) {
case "Built-in":
let keyarray = [];
for (let i = 0; i < accessKeys.length; i++) {
keyarray.push({
access_key: accessKeys[i],
secret_key: secretKeys[i],
});
}
dataIDP = {
keys: keyarray,
};
break;
case "OpenID":
dataIDP = {
oidc: {
url: openIDURL,
client_id: openIDClientID,
secret_id: openIDSecretID,
},
};
break;
case "AD":
dataIDP = {
active_directory: {
url: ADURL,
skip_tls_verification: ADSkipTLS,
server_insecure: ADServerInsecure,
username_format: "",
user_search_filter: ADUserNameFilter,
group_search_base_dn: ADGroupBaseDN,
group_search_filter: ADGroupSearchFilter,
group_name_attribute: ADNameAttribute,
},
};
break;
}
dataSend = {
...dataSend,
idp: { ...dataIDP },
};
api
.invoke("POST", `/api/v1/tenants`, dataSend)
.then((res) => {
@@ -495,6 +568,20 @@ const AddTenant = ({
},
],
},
{
label: "Pod Affinity",
advancedOnly: true,
componentRender: <Affinity />,
buttons: [
cancelButton,
{ label: "Back", type: "back", enabled: true },
{
label: "Next",
type: "next",
enabled: validPages.includes("affinity"),
},
],
},
{
label: "Identity Provider",
advancedOnly: true,

View File

@@ -0,0 +1,178 @@
// 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, useState, useCallback, Fragment } from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { AppState } from "../../../../../store";
import { updateAddField, isPageValid } from "../../actions";
import { setModalErrorSnackMessage } from "../../../../../actions";
import {
modalBasic,
wizardCommon,
} from "../../../Common/FormComponents/common/styleLibrary";
import { Grid } from "@material-ui/core";
import {
commonFormValidation,
IValidation,
} from "../../../../../utils/validationFunctions";
import RadioGroupSelector from "../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
import QueryMultiSelector from "../../../Common/FormComponents/QueryMultiSelector/QueryMultiSelector";
interface IAffinityProps {
classes: any;
podAffinity: string;
affinityLabels: string;
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
updateAddField: typeof updateAddField;
isPageValid: typeof isPageValid;
}
const styles = (theme: Theme) =>
createStyles({
buttonContainer: {
textAlign: "right",
},
...modalBasic,
...wizardCommon,
});
const Affinity = ({
classes,
podAffinity,
affinityLabels,
setModalErrorSnackMessage,
updateAddField,
isPageValid,
}: IAffinityProps) => {
const [validationErrors, setValidationErrors] = useState<any>({});
// Common
const updateField = useCallback(
(field: string, value: any) => {
updateAddField("affinity", field, value);
},
[updateAddField]
);
// Validation
useEffect(() => {
let customAccountValidation: IValidation[] = [];
if (podAffinity === "nodeSelector") {
let valid = true;
const splittedLabels = affinityLabels.split("&");
if (splittedLabels.length === 1 && splittedLabels[0] === "") {
valid = false;
}
splittedLabels.forEach((item: string, index: number) => {
const splitItem = item.split("=");
if (splitItem.length !== 2) {
valid = false;
}
if (index + 1 !== splittedLabels.length) {
if (splitItem[0] === "" || splitItem[1] === "") {
valid = false;
}
}
});
customAccountValidation = [
...customAccountValidation,
{
fieldKey: "labels",
required: true,
value: affinityLabels,
customValidation: !valid,
customValidationMessage:
"You need to add at least one label key-pair",
},
];
}
const commonVal = commonFormValidation(customAccountValidation);
isPageValid("affinity", Object.keys(commonVal).length === 0);
setValidationErrors(commonVal);
}, [isPageValid, podAffinity, affinityLabels]);
return (
<Fragment>
<div className={classes.headerElement}>
<h3 className={classes.h3Section}>Pod Affinity</h3>
<span className={classes.descriptionText}>
Configure how pods will be assigned to nodes
</span>
</div>
<Grid item xs={12}>
<RadioGroupSelector
currentSelection={podAffinity}
id="affinity-options"
name="affinity-options"
label="Type"
onChange={(e) => {
updateField("podAffinity", e.target.value);
}}
selectorOptions={[
{ label: "None", value: "none" },
{ label: "Default (Pod Anti-afinnity)", value: "default" },
{ label: "Node Selector", value: "nodeSelector" },
]}
/>
MinIO supports multiple configurations for Pod Afinnity
</Grid>
{podAffinity === "nodeSelector" && (
<Fragment>
<br />
<Grid item xs={12}>
<QueryMultiSelector
name="labels"
label="Labels"
elements={affinityLabels}
onChange={(vl: string) => {
updateField("affinityLabels", vl);
}}
keyPlaceholder="Label Key"
valuePlaceholder="Label Value"
tooltip="Labels to be used in nodeSelector assignation. Invalid key-pairs will be ignored"
withBorder
/>
<span className={classes.error}>{validationErrors["labels"]}</span>
</Grid>
</Fragment>
)}
</Fragment>
);
};
const mapState = (state: AppState) => ({
podAffinity: state.tenants.createTenant.fields.affinity.podAffinity,
affinityLabels: state.tenants.createTenant.fields.affinity.affinityLabels,
});
const connector = connect(mapState, {
setModalErrorSnackMessage,
updateAddField,
isPageValid,
});
export default withStyles(styles)(connector(Affinity));

View File

@@ -14,7 +14,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, { useEffect, useState, useCallback, Fragment } from "react";
import React, { Fragment, useCallback, useEffect, useState } from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Grid } from "@material-ui/core";
@@ -22,7 +22,7 @@ import {
modalBasic,
wizardCommon,
} from "../../../Common/FormComponents/common/styleLibrary";
import { updateAddField, isPageValid } from "../../actions";
import { isPageValid, updateAddField } from "../../actions";
import { AppState } from "../../../../../store";
import { clearValidationError } from "../../utils";
import {
@@ -31,10 +31,12 @@ import {
} from "../../../../../utils/validationFunctions";
import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import SelectWrapper from "../../../Common/FormComponents/SelectWrapper/SelectWrapper";
interface IConfigureProps {
updateAddField: typeof updateAddField;
isPageValid: typeof isPageValid;
storageClasses: any;
classes: any;
customImage: boolean;
imageName: string;
@@ -45,6 +47,18 @@ interface IConfigureProps {
imageRegistryPassword: string;
exposeMinIO: boolean;
exposeConsole: boolean;
prometheusCustom: boolean;
logSearchCustom: boolean;
logSearchVolumeSize: string;
logSearchSizeFactor: string;
prometheusVolumeSize: string;
prometheusSizeFactor: string;
logSearchSelectedStorageClass: string;
logSearchImage: string;
logSearchPostgresImage: string;
prometheusSelectedStorageClass: string;
prometheusImage: string;
selectedStorageClass: string;
}
const styles = (theme: Theme) =>
@@ -58,6 +72,7 @@ const styles = (theme: Theme) =>
const Configure = ({
classes,
storageClasses,
customImage,
imageName,
consoleImage,
@@ -67,8 +82,20 @@ const Configure = ({
imageRegistryPassword,
exposeMinIO,
exposeConsole,
prometheusCustom,
logSearchCustom,
logSearchVolumeSize,
logSearchSizeFactor,
logSearchImage,
logSearchPostgresImage,
prometheusVolumeSize,
prometheusSizeFactor,
logSearchSelectedStorageClass,
prometheusSelectedStorageClass,
prometheusImage,
updateAddField,
isPageValid,
selectedStorageClass,
}: IConfigureProps) => {
const [validationErrors, setValidationErrors] = useState<any>({});
@@ -84,24 +111,89 @@ const Configure = ({
useEffect(() => {
let customAccountValidation: IValidation[] = [];
if (prometheusCustom) {
customAccountValidation = [
...customAccountValidation,
{
fieldKey: "prometheus_storage_class",
required: true,
value: prometheusSelectedStorageClass,
customValidation: prometheusSelectedStorageClass === "",
customValidationMessage: "Field cannot be empty",
},
{
fieldKey: "prometheus_volume_size",
required: true,
value: prometheusVolumeSize,
customValidation:
prometheusVolumeSize === "" || parseInt(prometheusVolumeSize) <= 0,
customValidationMessage: `Volume size must be present and be greatter than 0`,
},
];
}
if (logSearchCustom) {
customAccountValidation = [
...customAccountValidation,
{
fieldKey: "log_search_storage_class",
required: true,
value: logSearchSelectedStorageClass,
customValidation: logSearchSelectedStorageClass === "",
customValidationMessage: "Field cannot be empty",
},
{
fieldKey: "log_search_volume_size",
required: true,
value: logSearchVolumeSize,
customValidation:
logSearchVolumeSize === "" || parseInt(logSearchVolumeSize) <= 0,
customValidationMessage: `Volume size must be present and be greatter than 0`,
},
];
}
if (customImage) {
customAccountValidation = [
...customAccountValidation,
{
fieldKey: "image",
required: true,
required: false,
value: imageName,
pattern: /^((.*?)\/(.*?):(.+))$/,
customPatternMessage: "Format must be of form: 'minio/minio:VERSION'",
},
{
fieldKey: "consoleImage",
required: true,
required: false,
value: consoleImage,
pattern: /^((.*?)\/(.*?):(.+))$/,
customPatternMessage:
"Format must be of form: 'minio/console:VERSION'",
},
{
fieldKey: "logSearchImage",
required: false,
value: logSearchImage,
pattern: /^((.*?)\/(.*?):(.+))$/,
customPatternMessage:
"Format must be of form: 'minio/logsearchapi:VERSION'",
},
{
fieldKey: "logSearchPostgresImage",
required: false,
value: logSearchPostgresImage,
pattern: /^((.*?)\/(.*?):(.+))$/,
customPatternMessage:
"Format must be of form: 'library/postgres:VERSION'",
},
{
fieldKey: "prometheusImage",
required: false,
value: prometheusImage,
pattern: /^((.*?)\/(.*?):(.+))$/,
customPatternMessage:
"Format must be of form: 'minio/prometheus:VERSION'",
},
];
if (customDockerhub) {
customAccountValidation = [
@@ -134,11 +226,45 @@ const Configure = ({
customImage,
imageName,
consoleImage,
logSearchImage,
logSearchPostgresImage,
prometheusImage,
customDockerhub,
imageRegistry,
imageRegistryUsername,
imageRegistryPassword,
isPageValid,
prometheusCustom,
logSearchCustom,
prometheusSelectedStorageClass,
prometheusVolumeSize,
logSearchSelectedStorageClass,
logSearchVolumeSize,
]);
useEffect(() => {
// New default values is current selection is invalid
if (storageClasses.length > 0) {
const filterPrometheus = storageClasses.filter(
(item: any) => item.value === prometheusSelectedStorageClass
);
if (filterPrometheus.length === 0) {
updateField("prometheusSelectedStorageClass", selectedStorageClass);
}
const filterLogSearch = storageClasses.filter(
(item: any) => item.value === logSearchSelectedStorageClass
);
if (filterLogSearch.length === 0) {
updateField("logSearchSelectedStorageClass", selectedStorageClass);
}
}
}, [
logSearchSelectedStorageClass,
prometheusSelectedStorageClass,
selectedStorageClass,
storageClasses,
updateField,
]);
const cleanValidation = (fieldName: string) => {
@@ -170,7 +296,7 @@ const Configure = ({
</Grid>
{customImage && (
<Fragment>
Please enter the MinIO image from dockerhub to use
Please enter the MinIO docker image to use
<Grid item xs={12}>
<InputBoxWrapper
id="image"
@@ -182,8 +308,7 @@ const Configure = ({
label="MinIO's Image"
value={imageName}
error={validationErrors["image"] || ""}
placeholder="E.g. minio/minio:RELEASE.2020-05-08T02-40-49Z"
required
placeholder="E.g. minio/minio:RELEASE.2021-04-22T15-44-28Z"
/>
</Grid>
<Grid item xs={12}>
@@ -197,8 +322,49 @@ const Configure = ({
label="Console's Image"
value={consoleImage}
error={validationErrors["consoleImage"] || ""}
placeholder="E.g. minio/console:v0.3.13"
required
placeholder="E.g. minio/console:v0.7.0"
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="logSearchImage"
name="logSearchImage"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("logSearchImage", e.target.value);
cleanValidation("logSearchImage");
}}
label="Log Search API's Image"
value={logSearchImage}
error={validationErrors["logSearchImage"] || ""}
placeholder="E.g. minio/logsearchapi:v4.0.9"
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="logSearchPostgresImage"
name="logSearchPostgresImage"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("logSearchPostgresImage", e.target.value);
cleanValidation("logSearchPostgresImage");
}}
label="Log Search Postgres's Image"
value={logSearchPostgresImage}
error={validationErrors["logSearchPostgresImage"] || ""}
placeholder="E.g. library/postgres:13"
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="prometheusImage"
name="prometheusImage"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("prometheusImage", e.target.value);
cleanValidation("prometheusImage");
}}
label="Prometheus Image"
value={prometheusImage}
error={validationErrors["prometheusImage"] || ""}
placeholder="E.g. quay.io/prometheus/prometheus:latest"
/>
</Grid>
</Fragment>
@@ -302,11 +468,132 @@ const Configure = ({
label={"Expose Console Service"}
/>
</Grid>
<div className={classes.headerElement}>
<h3 className={classes.h3Section}>Additional Configurations</h3>
<span className={classes.descriptionText}>
Configure Storage Classes & Storage size for Log Search and Prometheus
add-ons
</span>
</div>
<Grid item xs={12}>
<FormSwitchWrapper
value="logSearchConfig"
id="log_search_configuration"
name="log_search_configuration"
checked={logSearchCustom}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
updateField("logSearchCustom", checked);
}}
label={"Override Log Search defaults"}
/>
</Grid>
{logSearchCustom && (
<Fragment>
<Grid item xs={12}>
<SelectWrapper
id="log_search_storage_class"
name="log_search_storage_class"
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
updateField(
"logSearchSelectedStorageClass",
e.target.value as string
);
}}
label="Log Search Storage Class"
value={logSearchSelectedStorageClass}
options={storageClasses}
disabled={storageClasses.length < 1}
/>
</Grid>
<Grid item xs={12}>
<div className={classes.multiContainer}>
<div>
<InputBoxWrapper
type="number"
id="log_search_volume_size"
name="log_search_volume_size"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("logSearchVolumeSize", e.target.value);
cleanValidation("log_search_volume_size");
}}
label="Storage Size [Gi]"
value={logSearchVolumeSize}
required
error={validationErrors["log_search_volume_size"] || ""}
min="0"
/>
</div>
</div>
</Grid>
<br />
</Fragment>
)}
<Grid item xs={12}>
<FormSwitchWrapper
value="prometheusConfig"
id="prometheus_configuration"
name="prometheus_configuration"
checked={prometheusCustom}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
updateField("prometheusCustom", checked);
}}
label={"Override Prometheus defaults"}
/>
</Grid>
{prometheusCustom && (
<Fragment>
<Grid item xs={12}>
<SelectWrapper
id="prometheus_storage_class"
name="prometheus_storage_class"
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
updateField(
"prometheusSelectedStorageClass",
e.target.value as string
);
}}
label="Prometheus Storage Class"
value={prometheusSelectedStorageClass}
options={storageClasses}
disabled={storageClasses.length < 1}
/>
</Grid>
<Grid item xs={12}>
<div className={classes.multiContainer}>
<div>
<InputBoxWrapper
type="number"
id="prometheus_volume_size"
name="prometheus_volume_size"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("prometheusVolumeSize", e.target.value);
cleanValidation("prometheus_volume_size");
}}
label="Storage Size [Gi]"
value={prometheusVolumeSize}
required
error={validationErrors["prometheus_volume_size"] || ""}
min="0"
/>
</div>
</div>
</Grid>
<br />
</Fragment>
)}
</Fragment>
);
};
const mapState = (state: AppState) => ({
storageClasses: state.tenants.createTenant.storageClasses,
customImage: state.tenants.createTenant.fields.configure.customImage,
imageName: state.tenants.createTenant.fields.configure.imageName,
consoleImage: state.tenants.createTenant.fields.configure.consoleImage,
@@ -318,6 +605,27 @@ const mapState = (state: AppState) => ({
state.tenants.createTenant.fields.configure.imageRegistryPassword,
exposeMinIO: state.tenants.createTenant.fields.configure.exposeMinIO,
exposeConsole: state.tenants.createTenant.fields.configure.exposeConsole,
prometheusCustom:
state.tenants.createTenant.fields.configure.prometheusCustom,
logSearchCustom: state.tenants.createTenant.fields.configure.logSearchCustom,
logSearchVolumeSize:
state.tenants.createTenant.fields.configure.logSearchVolumeSize,
logSearchSizeFactor:
state.tenants.createTenant.fields.configure.logSearchSizeFactor,
prometheusVolumeSize:
state.tenants.createTenant.fields.configure.prometheusVolumeSize,
prometheusSizeFactor:
state.tenants.createTenant.fields.configure.prometheusSizeFactor,
logSearchSelectedStorageClass:
state.tenants.createTenant.fields.configure.logSearchSelectedStorageClass,
logSearchImage: state.tenants.createTenant.fields.configure.logSearchImage,
logSearchPostgresImage:
state.tenants.createTenant.fields.configure.logSearchPostgresImage,
prometheusSelectedStorageClass:
state.tenants.createTenant.fields.configure.prometheusSelectedStorageClass,
prometheusImage: state.tenants.createTenant.fields.configure.prometheusImage,
selectedStorageClass:
state.tenants.createTenant.fields.nameTenant.selectedStorageClass,
});
const connector = connect(mapState, {

View File

@@ -14,28 +14,33 @@
// 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, useCallback, Fragment } from "react";
import React, { Fragment, useCallback, useEffect, useState } from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Grid, Typography } from "@material-ui/core";
import { Grid, IconButton, Tooltip, Typography } from "@material-ui/core";
import CasinoIcon from "@material-ui/icons/Casino";
import DeleteIcon from "@material-ui/icons/Delete";
import {
modalBasic,
wizardCommon,
} from "../../../Common/FormComponents/common/styleLibrary";
import { updateAddField, isPageValid } from "../../actions";
import { isPageValid, updateAddField } from "../../actions";
import {
commonFormValidation,
IValidation,
} from "../../../../../utils/validationFunctions";
import { AppState } from "../../../../../store";
import { clearValidationError } from "../../utils";
import { clearValidationError, getRandomString } from "../../utils";
import RadioGroupSelector from "../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import AddIcon from "@material-ui/icons/Add";
interface IIdentityProviderProps {
classes: any;
idpSelection: string;
accessKeys: string[];
secretKeys: string[];
openIDURL: string;
openIDClientID: string;
openIDSecretID: string;
@@ -55,6 +60,18 @@ const styles = (theme: Theme) =>
buttonContainer: {
textAlign: "right",
},
shortened: {
gridTemplateColumns: "auto auto 50px 50px",
display: "grid",
gridGap: 20,
},
buttonTray: {
gridTemplateColumns: "auto auto 10px 10px",
display: "grid",
gridGap: 0,
height: 16,
marginTop: 12,
},
...modalBasic,
...wizardCommon,
});
@@ -62,6 +79,8 @@ const styles = (theme: Theme) =>
const IdentityProvider = ({
classes,
idpSelection,
accessKeys,
secretKeys,
openIDURL,
openIDClientID,
openIDSecretID,
@@ -77,13 +96,22 @@ const IdentityProvider = ({
}: IIdentityProviderProps) => {
const [validationErrors, setValidationErrors] = useState<any>({});
// Common
const updateField = useCallback(
(field: string, value: any) => {
updateAddField("identityProvider", field, value);
},
[updateAddField]
);
const updateUserField = (index: number, value: string) => {
const newUserField = [...accessKeys];
newUserField[index] = value;
updateField("accessKeys", newUserField);
};
const updatePwordField = (index: number, value: string) => {
const newUserField = [...secretKeys];
newUserField[index] = value;
updateField("secretKeys", newUserField);
};
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
@@ -95,10 +123,23 @@ const IdentityProvider = ({
let customIDPValidation: IValidation[] = [];
if (idpSelection === "Built-in") {
isPageValid("identityProvider", true);
setValidationErrors({});
return;
customIDPValidation = [...customIDPValidation];
for (var i = 0; i < accessKeys.length; i++) {
customIDPValidation.push({
fieldKey: `accesskey-${i.toString()}`,
required: true,
value: accessKeys[i],
pattern: /^[a-zA-Z0-9-]{8,63}$/,
customPatternMessage: "Keys must be at least length 8",
});
customIDPValidation.push({
fieldKey: `secretkey-${i.toString()}`,
required: true,
value: secretKeys[i],
pattern: /^[a-zA-Z0-9-]{8,63}$/,
customPatternMessage: "Keys must be at least length 8",
});
}
}
if (idpSelection === "OpenID") {
@@ -160,6 +201,8 @@ const IdentityProvider = ({
setValidationErrors(commonVal);
}, [
idpSelection,
accessKeys,
secretKeys,
openIDURL,
openIDClientID,
openIDSecretID,
@@ -170,7 +213,89 @@ const IdentityProvider = ({
ADNameAttribute,
isPageValid,
]);
let inputs = null;
if (idpSelection === "Built-in") {
inputs = accessKeys.map((_, index) => {
return (
<Fragment key={`identityField-${index.toString()}`}>
<div className={classes.shortened}>
<InputBoxWrapper
id={`accesskey-${index.toString()}`}
label={""}
placeholder={"Access Key"}
name={`accesskey-${index.toString()}`}
value={accessKeys[index]}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateUserField(index, e.target.value);
cleanValidation(`accesskey-${index.toString()}`);
}}
index={index}
key={`csv-accesskey-${index.toString()}`}
error={validationErrors[`accesskey-${index.toString()}`] || ""}
/>
<InputBoxWrapper
id={`secretkey-${index.toString()}`}
label={""}
placeholder={"Secret Key"}
name={`secretkey-${index.toString()}`}
value={secretKeys[index]}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updatePwordField(index, e.target.value);
cleanValidation(`secretkey-${index.toString()}`);
}}
index={index}
key={`csv-secretkey-${index.toString()}`}
error={validationErrors[`secretkey-${index.toString()}`] || ""}
/>
<div className={classes.buttonTray}>
<Tooltip title="Add User" aria-label="add">
<IconButton
size={"small"}
onClick={() => {
accessKeys.push("");
secretKeys.push("");
updateUserField(accessKeys.length - 1, "");
updatePwordField(secretKeys.length - 1, "");
}}
>
<AddIcon />
</IconButton>
</Tooltip>
<Tooltip title="Randomize Credentials" aria-label="add">
<IconButton
onClick={() => {
updateUserField(index, getRandomString(16));
updatePwordField(index, getRandomString(32));
}}
size={"small"}
>
<CasinoIcon />
</IconButton>
</Tooltip>
<Tooltip title="Remove" aria-label="add">
<IconButton
size={"small"}
style={{ marginLeft: 16 }}
onClick={() => {
if (accessKeys.length > 1) {
accessKeys.splice(index, 1);
secretKeys.splice(index, 1);
updateUserField(
accessKeys.length - 1,
accessKeys[accessKeys.length - 1]
);
}
}}
>
<DeleteIcon />
</IconButton>
</Tooltip>
</div>
</div>
</Fragment>
);
});
}
return (
<Fragment>
<div className={classes.headerElement}>
@@ -195,9 +320,9 @@ const IdentityProvider = ({
{ label: "Active Directory", value: "AD" },
]}
/>
MinIO supports both OpenID and Active Directory
</Grid>
Add additional users
</Grid>{" "}
{idpSelection === "Built-in" && <Fragment>{inputs}</Fragment>}
{idpSelection === "OpenID" && (
<Fragment>
<Grid item xs={12}>
@@ -367,6 +492,8 @@ const IdentityProvider = ({
const mapState = (state: AppState) => ({
idpSelection: state.tenants.createTenant.fields.identityProvider.idpSelection,
accessKeys: state.tenants.createTenant.fields.identityProvider.accessKeys,
secretKeys: state.tenants.createTenant.fields.identityProvider.secretKeys,
openIDURL: state.tenants.createTenant.fields.identityProvider.openIDURL,
openIDClientID:
state.tenants.createTenant.fields.identityProvider.openIDClientID,

View File

@@ -58,6 +58,7 @@ const styles = (theme: Theme) =>
interface INameTenantScreen {
classes: any;
storageClasses: Opts[];
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
setAdvancedMode: typeof setAdvancedMode;
updateAddField: typeof updateAddField;
isPageValid: typeof isPageValid;
@@ -81,8 +82,13 @@ const NameTenant = ({
setStorageClassesList,
setLimitSize,
isPageValid,
setModalErrorSnackMessage,
}: INameTenantScreen) => {
const [validationErrors, setValidationErrors] = useState<any>({});
const [emptyNamespace, setEmptyNamespace] = useState<boolean>(true);
const [loadingNamespaceInfo, setLoadingNamespaceInfo] = useState<boolean>(
false
);
// Common
const updateField = useCallback(
@@ -97,32 +103,62 @@ const NameTenant = ({
updateField("selectedStorageClass", "");
setStorageClassesList([]);
// Empty tenantValidation
api
.invoke(
"GET",
`/api/v1/namespaces/${namespace}/resourcequotas/${namespace}-storagequota`
)
.then((res: IQuotas) => {
const elements: IQuotaElement[] = get(res, "elements", []);
setLimitSize(getLimitSizes(res));
.invoke("GET", `/api/v1/namespaces/${namespace}/tenants`)
.then((res: any[]) => {
const tenantsList = get(res, "tenants", []);
const newStorage = elements.map((storageClass: any) => {
const name = get(storageClass, "name", "").split(
".storageclass.storage.k8s.io/requests.storage"
)[0];
return { label: name, value: name };
});
setStorageClassesList(newStorage);
if (newStorage.length > 0) {
updateField("selectedStorageClass", newStorage[0].value);
if (tenantsList && tenantsList.length > 0) {
setEmptyNamespace(false);
setLoadingNamespaceInfo(false);
return;
}
setEmptyNamespace(true);
// Storagequotas retrieval
api
.invoke(
"GET",
`/api/v1/namespaces/${namespace}/resourcequotas/${namespace}-storagequota`
)
.then((res: IQuotas) => {
const elements: IQuotaElement[] = get(res, "elements", []);
setLimitSize(getLimitSizes(res));
const newStorage = elements.map((storageClass: any) => {
const name = get(storageClass, "name", "").split(
".storageclass.storage.k8s.io/requests.storage"
)[0];
return { label: name, value: name };
});
setStorageClassesList(newStorage);
if (newStorage.length > 0) {
updateField("selectedStorageClass", newStorage[0].value);
}
setLoadingNamespaceInfo(false);
})
.catch((err: any) => {
setLoadingNamespaceInfo(false);
console.error("Namespace error: ", err);
});
})
.catch((err: any) => {
console.error(err);
setModalErrorSnackMessage(
"Error validating if namespace already has tenants"
);
});
}, [namespace, setLimitSize, setStorageClassesList, updateField]);
}, [
namespace,
setLimitSize,
setModalErrorSnackMessage,
setStorageClassesList,
updateField,
]);
const debounceNamespace = useMemo(
() => debounce(getNamespaceInformation, 500),
@@ -132,6 +168,7 @@ const NameTenant = ({
useEffect(() => {
if (namespace !== "") {
debounceNamespace();
setLoadingNamespaceInfo(true);
// Cancel previous debounce calls during useEffect cleanup.
return debounceNamespace.cancel;
@@ -140,6 +177,21 @@ const NameTenant = ({
// Validation
useEffect(() => {
let customNamespaceError = false;
let errorMessage = "";
if (!emptyNamespace && !loadingNamespaceInfo) {
customNamespaceError = true;
errorMessage = "You can only create one tenant per namespace";
} else if (
storageClasses.length < 1 &&
emptyNamespace &&
!loadingNamespaceInfo
) {
customNamespaceError = true;
errorMessage = "Please enter a valid namespace";
}
const commonValidation = commonFormValidation([
{
fieldKey: "tenant-name",
@@ -153,8 +205,8 @@ const NameTenant = ({
fieldKey: "namespace",
required: true,
value: namespace,
customValidation: storageClasses.length < 1,
customValidationMessage: "Please enter a valid namespace",
customValidation: customNamespaceError,
customValidationMessage: errorMessage,
},
]);
@@ -166,7 +218,14 @@ const NameTenant = ({
isPageValid("nameTenant", isValid);
setValidationErrors(commonValidation);
}, [storageClasses, namespace, tenantName, isPageValid]);
}, [
storageClasses,
namespace,
tenantName,
isPageValid,
emptyNamespace,
loadingNamespaceInfo,
]);
const frmValidationCleanup = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));

View File

@@ -283,6 +283,7 @@ const TenantSize = ({
memorySize,
limitSize,
selectedStorageClass,
isPageValid,
]);
/* End Validation of pages */

View File

@@ -115,7 +115,7 @@ const DeleteTenant = ({
onClick={removeRecord}
color="secondary"
autoFocus
disabled={retypeTenant === ""}
disabled={retypeTenant !== selectedTenant.name}
>
Delete
</Button>

View File

@@ -231,10 +231,6 @@ const ListTenants = ({
<SlideOptions
slideOptions={[
<Fragment>
<Grid item xs={12} className={classes.customTitle}>
Tenants List
</Grid>
<Grid item xs={12} className={classes.tenantsContainer}>
<Grid item xs={12} className={classes.actionsTray}>
<TextField

Some files were not shown because too many files have changed in this diff Show More