diff --git a/cmd/admin-trace.go b/cmd/admin-trace.go
index 988ab06bb7..042d7ed607 100644
--- a/cmd/admin-trace.go
+++ b/cmd/admin-trace.go
@@ -900,6 +900,7 @@ type statItem struct {
CallStatsCount int `json:"callStatsCount,omitempty"`
CallStats callStats `json:"callStats,omitempty"`
TTFB time.Duration `json:"ttfb,omitempty"`
+ MaxTTFB time.Duration `json:"maxTTFB,omitempty"`
MaxDur time.Duration `json:"maxDuration"`
MinDur time.Duration `json:"minDuration"`
}
@@ -939,6 +940,9 @@ func (s *statTrace) add(t madmin.ServiceTraceInfo) {
if got.MaxDur < t.Trace.Duration {
got.MaxDur = t.Trace.Duration
}
+ if got.MaxTTFB < t.Trace.HTTP.CallStats.TimeToFirstByte {
+ got.MaxTTFB = t.Trace.HTTP.CallStats.TimeToFirstByte
+ }
if got.MinDur <= 0 {
got.MinDur = t.Trace.Duration
}
@@ -1078,9 +1082,10 @@ func (m *traceStatsUI) View() string {
console.Colorize("metrics-top-title", "Count"),
console.Colorize("metrics-top-title", "RPM"),
console.Colorize("metrics-top-title", "Avg Time"),
- console.Colorize("metrics-top-title", "TTFB Time"),
console.Colorize("metrics-top-title", "Min Time"),
console.Colorize("metrics-top-title", "Max Time"),
+ console.Colorize("metrics-top-title", "Avg TTFB"),
+ console.Colorize("metrics-top-title", "Max TTFB"),
console.Colorize("metrics-top-title", "Errors"),
console.Colorize("metrics-top-title", "RX Avg"),
console.Colorize("metrics-top-title", "TX Avg"),
@@ -1128,9 +1133,10 @@ func (m *traceStatsUI) View() string {
console.Colorize("metrics-number-secondary", fmt.Sprintf("(%0.1f%%)", float64(v.Count)/float64(totalCnt)*100)),
console.Colorize("metrics-number", fmt.Sprintf("%0.1f", float64(v.Count)/dur.Minutes())),
console.Colorize(avgColor, fmt.Sprintf("%v", avg.Round(time.Microsecond))),
- console.Colorize(avgColor, fmt.Sprintf("%v", avgTTFB.Round(time.Microsecond))),
console.Colorize(minColor, v.MinDur),
console.Colorize(maxColor, v.MaxDur),
+ console.Colorize(avgColor, fmt.Sprintf("%v", avgTTFB.Round(time.Microsecond))),
+ console.Colorize(maxColor, v.MaxTTFB),
errs,
rx,
tx,
diff --git a/cmd/client-admin.go b/cmd/client-admin.go
index 6f62413952..eb3b9be8a4 100644
--- a/cmd/client-admin.go
+++ b/cmd/client-admin.go
@@ -24,13 +24,18 @@ import (
"net"
"net/http"
"net/url"
+ "os"
"sync"
"time"
"github.com/klauspost/compress/gzhttp"
+
+ "github.com/minio/pkg/v2/env"
+
"github.com/mattn/go-ieproxy"
"github.com/minio/madmin-go/v3"
"github.com/minio/mc/pkg/httptracer"
+ "github.com/minio/mc/pkg/limiter"
"github.com/minio/mc/pkg/probe"
"github.com/minio/minio-go/v7/pkg/credentials"
)
@@ -67,8 +72,97 @@ func NewAdminFactory() func(config *Config) (*madmin.AdminClient, *probe.Error)
var api *madmin.AdminClient
var found bool
if api, found = clientCache[confSum]; !found {
+
+ var transport http.RoundTripper
+
+ if config.Transport != nil {
+ transport = config.Transport
+ } else {
+ tr := &http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ DialContext: newCustomDialContext(config),
+ MaxIdleConnsPerHost: 1024,
+ WriteBufferSize: 32 << 10, // 32KiB moving up from 4KiB default
+ ReadBufferSize: 32 << 10, // 32KiB moving up from 4KiB default
+ IdleConnTimeout: 90 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: 10 * time.Second,
+ DisableCompression: true,
+ // Set this value so that the underlying transport round-tripper
+ // doesn't try to auto decode the body of objects with
+ // content-encoding set to `gzip`.
+ //
+ // Refer:
+ // https://golang.org/src/net/http/transport.go?h=roundTrip#L1843
+ }
+ if useTLS {
+ // Keep TLS config.
+ tlsConfig := &tls.Config{
+ RootCAs: globalRootCAs,
+ // Can't use SSLv3 because of POODLE and BEAST
+ // Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
+ // Can't use TLSv1.1 because of RC4 cipher usage
+ MinVersion: tls.VersionTLS12,
+ }
+ if config.Insecure {
+ tlsConfig.InsecureSkipVerify = true
+ }
+ tr.TLSClientConfig = tlsConfig
+
+ // Because we create a custom TLSClientConfig, we have to opt-in to HTTP/2.
+ // See https://github.com/golang/go/issues/14275
+ //
+ // TODO: Enable http2.0 when upstream issues related to HTTP/2 are fixed.
+ //
+ // if e = http2.ConfigureTransport(tr); e != nil {
+ // return nil, probe.NewError(e)
+ // }
+ }
+ transport = tr
+ }
+
+ transport = limiter.New(config.UploadLimit, config.DownloadLimit, transport)
+
+ if config.Debug {
+ transport = httptracer.GetNewTraceTransport(newTraceV4(), transport)
+ }
+
+ transport = gzhttp.Transport(transport)
+
+ var credsChain []credentials.Provider
+
+ // if an STS endpoint is set, we will add that to the chain
+ if stsEndpoint := env.Get("MC_STS_ENDPOINT", ""); stsEndpoint != "" {
+ // set AWS_WEB_IDENTITY_TOKEN_FILE is MC_WEB_IDENTITY_TOKEN_FILE is set
+ if val := env.Get("MC_WEB_IDENTITY_TOKEN_FILE", ""); val != "" {
+ os.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", val)
+ }
+
+ stsEndpointURL, err := url.Parse(stsEndpoint)
+ if err != nil {
+ return nil, probe.NewError(fmt.Errorf("Error parsing sts endpoint: %v", err))
+ }
+ credsSts := &credentials.IAM{
+ Client: &http.Client{
+ Transport: transport,
+ },
+ Endpoint: stsEndpointURL.String(),
+ }
+ credsChain = append(credsChain, credsSts)
+ }
+
+ // V4 Credentials
+ credsV4 := &credentials.Static{
+ Value: credentials.Value{
+ AccessKeyID: config.AccessKey,
+ SecretAccessKey: config.SecretKey,
+ SessionToken: config.SessionToken,
+ SignerType: credentials.SignatureV4,
+ },
+ }
+ credsChain = append(credsChain, credsV4)
// Admin API only supports signature v4.
- creds := credentials.NewStaticV4(config.AccessKey, config.SecretKey, config.SessionToken)
+ creds := credentials.NewChainCredentials(credsChain)
// Not found. Instantiate a new MinIO
var e error
@@ -80,34 +174,6 @@ func NewAdminFactory() func(config *Config) (*madmin.AdminClient, *probe.Error)
return nil, probe.NewError(e)
}
- // Keep TLS config.
- tlsConfig := &tls.Config{
- RootCAs: globalRootCAs,
- // Can't use SSLv3 because of POODLE and BEAST
- // Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
- // Can't use TLSv1.1 because of RC4 cipher usage
- MinVersion: tls.VersionTLS12,
- }
- if config.Insecure {
- tlsConfig.InsecureSkipVerify = true
- }
-
- var transport http.RoundTripper = &http.Transport{
- Proxy: ieproxy.GetProxyFunc(),
- DialContext: newCustomDialContext(config),
- MaxIdleConnsPerHost: 256,
- IdleConnTimeout: 90 * time.Second,
- TLSHandshakeTimeout: 10 * time.Second,
- ExpectContinueTimeout: 10 * time.Second,
- TLSClientConfig: tlsConfig,
- DisableCompression: true,
- }
- transport = gzhttp.Transport(transport)
-
- if config.Debug {
- transport = httptracer.GetNewTraceTransport(newTraceV4(), transport)
- }
-
// Set custom transport.
api.SetCustomTransport(transport)
diff --git a/cmd/client-admin_test.go b/cmd/client-admin_test.go
new file mode 100644
index 0000000000..46f6ce6673
--- /dev/null
+++ b/cmd/client-admin_test.go
@@ -0,0 +1,127 @@
+// Copyright (c) 2015-2023 MinIO, Inc.
+//
+// # This file is part of MinIO Object Storage stack
+//
+// 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 .
+
+package cmd
+
+import (
+ "bytes"
+ "context"
+ "io"
+ "log"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "strconv"
+
+ checkv1 "gopkg.in/check.v1"
+)
+
+type adminPolicyHandler struct {
+ endpoint string
+ name string
+ policy []byte
+}
+
+func (h adminPolicyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if ak := r.Header.Get("Authorization"); len(ak) == 0 {
+ w.WriteHeader(http.StatusForbidden)
+ return
+ }
+ switch {
+ case r.Method == "PUT":
+ length, e := strconv.Atoi(r.Header.Get("Content-Length"))
+ if e != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ var buffer bytes.Buffer
+ if _, e = io.CopyN(&buffer, r.Body, int64(length)); e != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ if len(h.policy) != buffer.Len() {
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ w.Header().Set("Content-Length", "0")
+ w.WriteHeader(http.StatusOK)
+
+ default:
+ w.WriteHeader(http.StatusForbidden)
+ }
+}
+
+func (s *TestSuite) TestAdminSTSOperation(c *checkv1.C) {
+ sts := stsHandler{
+ endpoint: "/",
+ jwt: []byte("eyJhbGciOiJSUzI1NiIsImtpZCI6Inc0dFNjMEc5Tk0wQWhGaWJYaWIzbkpRZkRKeDc1dURRTUVpOTNvTHJ0OWcifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzMxMTg3NzEwLCJpYXQiOjE2OTk2NTE3MTAsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJtaW5pby10ZW5hbnQtMSIsInBvZCI6eyJuYW1lIjoic2V0dXAtYnVja2V0LXQ4eGdjIiwidWlkIjoiNjZhYjlkZWItNzkwMC00YTFlLTgzMDgtMTkwODIwZmQ3NDY5In0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJtYy1qb2Itc2EiLCJ1aWQiOiI3OTc4NzJjZC1kMjkwLTRlM2EtYjYyMC00ZGFkYzZhNzUyMTYifSwid2FybmFmdGVyIjoxNjk5NjU1MzE3fSwibmJmIjoxNjk5NjUxNzEwLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6bWluaW8tdGVuYW50LTE6bWMtam9iLXNhIn0.rY7dpAh8GBTViH9Ges7tRhgyihdFWEN0DwXchelmZg58VOI526S-YfbCqrxksTs8Iu0fp1rmk1cUj7FGDh3AOv2RphHjoWci1802zKkHgH0iOEbKMp3jHXwfyHda8CyrSCPycGzClueCf1ae91wd_0lgK9lOR1qqY1HuDeXqSEAUIGrfh1VcP2n95Zc07EY-Uh3XjJE4drtgusACEK5n3P3WtN9s0m0GomEGQzF5ZJczxLGpHBKMQ5VDhMksVKdBAsx9xHgSx84aUhKQViYilAL-8PRj-RZA9s_IpEymAh5R37dKzAO8Fqq0nG7fVbH_ifzw3xhHiG92BhHldBDqEQ"),
+ }
+
+ tmpfile, errFs := os.CreateTemp("", "jwt")
+ if errFs != nil {
+ log.Fatal(errFs)
+ }
+ defer os.Remove(tmpfile.Name()) // clean up
+
+ if _, errFs := tmpfile.Write(sts.jwt); errFs != nil {
+ log.Fatal(errFs)
+ }
+ if errFs := tmpfile.Close(); errFs != nil {
+ log.Fatal(errFs)
+ }
+
+ stsServer := httptest.NewServer(sts)
+ defer stsServer.Close()
+ os.Setenv("MC_STS_ENDPOINT", stsServer.URL+sts.endpoint)
+ os.Setenv("MC_WEB_IDENTITY_TOKEN_FILE", tmpfile.Name())
+ handler := adminPolicyHandler{
+ endpoint: "/minio/admin/v3/add-canned-policy?name=",
+ name: "test",
+ policy: []byte(`
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": [
+ "s3:*"
+ ],
+ "Resource": [
+ "arn:aws:s3:::test-bucket",
+ "arn:aws:s3:::test-bucket/*"
+ ]
+ }
+ ]
+
+}`),
+ }
+ server := httptest.NewServer(handler)
+ defer server.Close()
+
+ conf := new(Config)
+ conf.Debug = true
+ conf.Insecure = true
+ conf.HostURL = server.URL + handler.endpoint + handler.name
+ s3c, err := s3AdminNew(conf)
+ c.Assert(err, checkv1.IsNil)
+
+ policyErr := s3c.AddCannedPolicy(context.Background(), handler.name, handler.policy)
+ c.Assert(policyErr, checkv1.IsNil)
+}
diff --git a/cmd/client-s3.go b/cmd/client-s3.go
index 8e79349c0b..1db1dc9c2d 100644
--- a/cmd/client-s3.go
+++ b/cmd/client-s3.go
@@ -132,13 +132,19 @@ func newFactory() func(config *Config) (Client, *probe.Error) {
useTLS = false
}
+ // Save if target supports virtual host style.
+ hostName := targetURL.Host
+
+ // Generate a hash out of s3Conf.
+ confHash := fnv.New32a()
+ confHash.Write([]byte(hostName + config.AccessKey + config.SecretKey + config.SessionToken))
+ confSum := confHash.Sum32()
+
// Instantiate s3
s3Clnt := &S3Client{}
// Save the target URL.
s3Clnt.targetURL = targetURL
- // Save if target supports virtual host style.
- hostName := targetURL.Host
s3Clnt.virtualStyle = isVirtualHostStyle(hostName, config.Lookup)
isS3AcceleratedEndpoint := isAmazonAccelerated(hostName)
@@ -149,11 +155,6 @@ func newFactory() func(config *Config) (Client, *probe.Error) {
}
}
- // Generate a hash out of s3Conf.
- confHash := fnv.New32a()
- confHash.Write([]byte(hostName + config.AccessKey + config.SecretKey + config.SessionToken))
- confSum := confHash.Sum32()
-
// Lookup previous cache by hash.
mutex.Lock()
defer mutex.Unlock()
diff --git a/cmd/client-s3_test.go b/cmd/client-s3_test.go
index 8fed981291..79065e024f 100644
--- a/cmd/client-s3_test.go
+++ b/cmd/client-s3_test.go
@@ -22,8 +22,10 @@ import (
"bytes"
"context"
"io"
+ "log"
"net/http"
"net/http/httptest"
+ "os"
"strconv"
minio "github.com/minio/minio-go/v7"
@@ -80,6 +82,11 @@ type objectHandler struct {
}
func (h objectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if ak := r.Header.Get("Authorization"); len(ak) == 0 {
+ w.WriteHeader(http.StatusForbidden)
+ return
+ }
+
switch {
case r.Method == "PUT":
// Handler for PUT object request.
@@ -156,6 +163,38 @@ func (h objectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
+type stsHandler struct {
+ endpoint string
+ jwt []byte
+}
+
+func (h stsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if err := ParseForm(r); err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ switch {
+ case r.Method == "POST":
+ token := r.Form.Get("WebIdentityToken")
+ if token == string(h.jwt) {
+ response := []byte("7NL5BR739GUQ0ZOD4JNBA2mxZSxPnHNhSduedUHczsXZpVSSssOLpDruUmTV0001-01-01T00:00:00ZeyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiI3Tkw1QlI3MzlHVVEwWk9ENEpOQiIsImV4cCI6MTY5OTYwMzMwNiwicGFyZW50IjoibWluaW8iLCJzZXNzaW9uUG9saWN5IjoiZXlKV1pYSnphVzl1SWpvaU1qQXhNaTB4TUMweE55SXNJbE4wWVhSbGJXVnVkQ0k2VzNzaVJXWm1aV04wSWpvaVFXeHNiM2NpTENKQlkzUnBiMjRpT2xzaVlXUnRhVzQ2S2lKZGZTeDdJa1ZtWm1WamRDSTZJa0ZzYkc5M0lpd2lRV04wYVc5dUlqcGJJbXR0Y3pvcUlsMTlMSHNpUldabVpXTjBJam9pUVd4c2IzY2lMQ0pCWTNScGIyNGlPbHNpY3pNNktpSmRMQ0pTWlhOdmRYSmpaU0k2V3lKaGNtNDZZWGR6T25Nek9qbzZLaUpkZlYxOSJ9.uuE_x7PO8QoPfUk9KzUELoAqxihIknZAvJLl5aYJjwpSjJYFTPLp6EvuyJX2hc18s9HzeiJ-vU0dPzsy50dXmg")
+ w.Header().Set("Content-Length", strconv.Itoa(len(response)))
+ w.Header().Set("Content-Type", "application/xml")
+ w.Header().Set("Server", "MinIO")
+ w.Write(response)
+ w.WriteHeader(http.StatusOK)
+ return
+ } else {
+ response := []byte("AccessDenied
Access denied: Invalid Token")
+ w.Header().Set("Content-Length", strconv.Itoa(len(response)))
+ w.Header().Set("Content-Type", "application/xml")
+ w.Write(response)
+ w.WriteHeader(http.StatusForbidden)
+ return
+ }
+ }
+}
+
// Test bucket operations.
func (s *TestSuite) TestBucketOperations(c *checkv1.C) {
bucket := bucketHandler{
@@ -240,6 +279,52 @@ func (s *TestSuite) TestObjectOperations(c *checkv1.C) {
}
}
+func (s *TestSuite) TestSTSOperation(c *checkv1.C) {
+ sts := stsHandler{
+ endpoint: "/",
+ jwt: []byte("eyJhbGciOiJSUzI1NiIsImtpZCI6Inc0dFNjMEc5Tk0wQWhGaWJYaWIzbkpRZkRKeDc1dURRTUVpOTNvTHJ0OWcifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzMxMTIyNjg0LCJpYXQiOjE2OTk1ODY2ODQsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJtaW5pby10ZW5hbnQtMSIsInBvZCI6eyJuYW1lIjoic2V0dXAtYnVja2V0LXJ4aHhiIiwidWlkIjoiNmNhMzhjMmItYTdkMC00M2Y0LWE0NjMtZjdlNjU4MGUyZDdiIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJtYy1qb2Itc2EiLCJ1aWQiOiI3OTc4NzJjZC1kMjkwLTRlM2EtYjYyMC00ZGFkYzZhNzUyMTYifSwid2FybmFmdGVyIjoxNjk5NTkwMjkxfSwibmJmIjoxNjk5NTg2Njg0LCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6bWluaW8tdGVuYW50LTE6bWMtam9iLXNhIn0.fBJckmoQFyJ9bUgKZv6jzBESd9ccX_HFPPBZ17Gz_CsQ5wXrMqnvoMs1mcv6QKWsDsvSnWnw_tcW0cjvVkXb2mKmioKLzqV4ihGbiWzwk2e1xDohn8fizdQkf64bXpncjGdEGv8oi9A4300jfLMfg53POriMyEAQMeIDKPOI9qx913xjGni2w2H49mjLfnFnRaj9osvy17425dNIrMC6GDFq3rcq6Z_cdDmL18Jwsjy1xDsAhUzmOclr-VI3AeSnuD4fbf6jhbKE14qVUjLmIBf__B5NhESiaFNwxFYjonZyi357Nx93CD1wai28tNRSODx7BiPHLxk8SyzY0CP0sQ"),
+ }
+
+ tmpfile, errFs := os.CreateTemp("", "jwt")
+ if errFs != nil {
+ log.Fatal(errFs)
+ }
+ defer os.Remove(tmpfile.Name()) // clean up
+
+ if _, errFs := tmpfile.Write(sts.jwt); errFs != nil {
+ log.Fatal(errFs)
+ }
+ if errFs := tmpfile.Close(); errFs != nil {
+ log.Fatal(errFs)
+ }
+
+ stsServer := httptest.NewServer(sts)
+ defer stsServer.Close()
+ os.Setenv("MC_STS_ENDPOINT", stsServer.URL+sts.endpoint)
+ os.Setenv("MC_WEB_IDENTITY_TOKEN_FILE", tmpfile.Name())
+ object := objectHandler{
+ resource: "/bucket/object",
+ data: []byte("Hello, World"),
+ }
+ server := httptest.NewServer(object)
+ defer server.Close()
+
+ conf := new(Config)
+ conf.HostURL = server.URL + object.resource
+ s3c, err := S3New(conf)
+ c.Assert(err, checkv1.IsNil)
+
+ var reader io.Reader
+ reader = bytes.NewReader(object.data)
+ n, err := s3c.Put(context.Background(), reader, int64(len(object.data)), nil, PutOptions{
+ metadata: map[string]string{
+ "Content-Type": "application/octet-stream",
+ },
+ })
+ c.Assert(err, checkv1.IsNil)
+ c.Assert(n, checkv1.Equals, int64(len(object.data)))
+}
+
var testSelectCompressionTypeCases = []struct {
opts SelectObjectOpts
object string
diff --git a/cmd/common-methods.go b/cmd/common-methods.go
index f7d6a124d6..1d4dfe563b 100644
--- a/cmd/common-methods.go
+++ b/cmd/common-methods.go
@@ -654,3 +654,16 @@ func newClient(aliasedURL string) (Client, *probe.Error) {
}
return newClientFromAlias(alias, urlStrFull)
}
+
+// ParseForm parses a http.Request form and populates the array
+func ParseForm(r *http.Request) error {
+ if err := r.ParseForm(); err != nil {
+ return err
+ }
+ for k, v := range r.PostForm {
+ if _, ok := r.Form[k]; !ok {
+ r.Form[k] = v
+ }
+ }
+ return nil
+}
diff --git a/cmd/idp-ldap-accesskey-list.go b/cmd/idp-ldap-accesskey-list.go
index 14245804fa..be1b3f8971 100644
--- a/cmd/idp-ldap-accesskey-list.go
+++ b/cmd/idp-ldap-accesskey-list.go
@@ -64,7 +64,7 @@ EXAMPLES:
1. Get list of all users and associated access keys in local server (if admin)
{{.Prompt}} {{.HelpName}} local/
2. Get list of users in local server (if admin)
- {{.Prompt}} {{.HelpName}} local/ --users
+ {{.Prompt}} {{.HelpName}} local/ --users-only
3. Get list of all users and associated temporary access keys in play server (if admin)
{{.Prompt}} {{.HelpName}} play/ --temp-only
4. Get list of access keys associated with user 'bobfisher'
diff --git a/cmd/ilm-rule-add.go b/cmd/ilm-rule-add.go
index 823e7c2266..57ae31bc37 100644
--- a/cmd/ilm-rule-add.go
+++ b/cmd/ilm-rule-add.go
@@ -156,8 +156,9 @@ var ilmAddFlags = []cli.Flag{
Hidden: true,
},
cli.IntFlag{
- Name: "noncurrent-transition-newer",
- Usage: "number of noncurrent versions to retain in hot tier",
+ Name: "noncurrent-transition-newer",
+ Usage: "number of noncurrent versions to retain in hot tier",
+ Hidden: true,
},
cli.StringFlag{
Name: "noncurrentversion-transition-storage-class",