mirror of
https://github.com/pgsty/minio.git
synced 2026-08-10 08:13:28 +03:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6817c5ea58 | |||
| 4cd6ca02c7 | |||
| a5efcbab51 | |||
| 85be7b39ac | |||
| ebf3dda449 | |||
| 582953260b | |||
| 2d1ea86fc6 | |||
| 322385f1b6 | |||
| 1b38aed05f | |||
| a69c98e394 | |||
| 282c9f790a | |||
| 2eeb0e6a0b | |||
| 3ff5bf2369 | |||
| 69ee28a082 | |||
| d02deff3d7 | |||
| 3e78ea8acc | |||
| b54c0f0ef3 | |||
| c79358c67e | |||
| 75107d7698 | |||
| c4464e36c8 | |||
| d92db198d1 | |||
| 8bae956df6 | |||
| 7758524703 | |||
| c82fa2c829 | |||
| a51280fd20 | |||
| bd437c1c17 | |||
| 69fb68ef0b | |||
| 787dbaff36 | |||
| c50ae1fdbe | |||
| bde0f444db | |||
| 6a8298b137 | |||
| f19cbfad5c | |||
| 78f2183e70 |
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: MinIO Community Support
|
||||
url: https://slack.min.io
|
||||
about: Please ask and answer questions here.
|
||||
- name: MinIO SUBNET Support
|
||||
url: https://min.io/pricing
|
||||
about: Join this for Enterprise Support.
|
||||
@@ -0,0 +1,39 @@
|
||||
# Configuration for Lock Threads - https://github.com/dessant/lock-threads-app
|
||||
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 365
|
||||
|
||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
||||
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
|
||||
skipCreatedBefore: false
|
||||
|
||||
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
|
||||
exemptLabels: []
|
||||
|
||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
||||
lockLabel: false
|
||||
|
||||
# Comment to post before locking. Set to `false` to disable
|
||||
lockComment: >-
|
||||
|
||||
This thread has been automatically locked since there has not been
|
||||
any recent activity after it was closed. Please open a new issue for
|
||||
related bugs.
|
||||
|
||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
||||
setLockReason: true
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
only: issues
|
||||
|
||||
# Optionally, specify configuration settings just for `issues` or `pulls`
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - help-wanted
|
||||
# lockLabel: outdated
|
||||
|
||||
# pulls:
|
||||
# daysUntilLock: 30
|
||||
|
||||
# Repository to extend settings from
|
||||
# _extends: repo
|
||||
@@ -0,0 +1,59 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 90
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 30
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
||||
onlyLabels: []
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
exemptLabels:
|
||||
- "security"
|
||||
- "pending discussion"
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: false
|
||||
|
||||
# Set to true to ignore issues with an assignee (defaults to false)
|
||||
exemptAssignees: false
|
||||
|
||||
# Label to use when marking as stale
|
||||
staleLabel: stale
|
||||
|
||||
# Comment to post when marking as stale. Set to `false` to disable
|
||||
markComment: >-
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed after 21 days if no further activity
|
||||
occurs. Thank you for your contributions.
|
||||
# Comment to post when removing the stale label.
|
||||
# unmarkComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Comment to post when closing a stale Issue or Pull Request.
|
||||
# closeComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 1
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
# only: issues
|
||||
|
||||
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
||||
# pulls:
|
||||
# daysUntilStale: 30
|
||||
# markComment: >
|
||||
# This pull request has been automatically marked as stale because it has not had
|
||||
# recent activity. It will be closed if no further activity occurs. Thank you
|
||||
# for your contributions.
|
||||
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - confirmed
|
||||
@@ -1306,33 +1306,6 @@ https://github.com/aws/aws-sdk-go
|
||||
|
||||
================================================================
|
||||
|
||||
github.com/baiyubin/aliyun-sts-go-sdk
|
||||
https://github.com/baiyubin/aliyun-sts-go-sdk
|
||||
----------------------------------------------------------------
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Aliyun.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
================================================================
|
||||
|
||||
github.com/bcicen/jstream
|
||||
https://github.com/bcicen/jstream
|
||||
----------------------------------------------------------------
|
||||
@@ -16597,32 +16570,6 @@ SOFTWARE.
|
||||
|
||||
================================================================
|
||||
|
||||
github.com/satori/go.uuid
|
||||
https://github.com/satori/go.uuid
|
||||
----------------------------------------------------------------
|
||||
Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================
|
||||
|
||||
github.com/secure-io/sio-go
|
||||
https://github.com/secure-io/sio-go
|
||||
----------------------------------------------------------------
|
||||
|
||||
+2
-8
@@ -1,17 +1,11 @@
|
||||
FROM ubuntu:16.04
|
||||
FROM ubuntu:18.04
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
|
||||
ENV GOROOT /usr/local/go
|
||||
|
||||
ENV GOPATH /usr/local
|
||||
|
||||
ENV GOPATH /usr/local/gopath
|
||||
ENV PATH $GOPATH/bin:$GOROOT/bin:$PATH
|
||||
|
||||
ENV MINT_ROOT_DIR /mint
|
||||
|
||||
COPY mint /mint
|
||||
|
||||
RUN apt-get --yes update && apt-get --yes upgrade && \
|
||||
|
||||
+2
-2
@@ -57,7 +57,7 @@ RUN yarn test
|
||||
#-------------------------------------------------------------
|
||||
# Stage 3: Run Gateway Tests
|
||||
#-------------------------------------------------------------
|
||||
FROM ubuntu:16.04
|
||||
FROM ubuntu:18.04
|
||||
|
||||
COPY --from=0 /go/src/github.com/minio/minio/minio /usr/bin/minio
|
||||
COPY buildscripts/gateway-tests.sh /usr/bin/gateway-tests.sh
|
||||
@@ -66,7 +66,7 @@ COPY mint /mint
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
ENV LANG C.UTF-8
|
||||
ENV GOROOT /usr/local/go
|
||||
ENV GOPATH /usr/local
|
||||
ENV GOPATH /usr/local/gopath
|
||||
ENV PATH $GOPATH/bin:$GOROOT/bin:$PATH
|
||||
ENV SIMPLE_CI 1
|
||||
ENV MINT_ROOT_DIR /mint
|
||||
|
||||
@@ -48,7 +48,7 @@ function main()
|
||||
SERVER_ENDPOINT=127.0.0.1:24240 ENABLE_HTTPS=0 ACCESS_KEY=minio \
|
||||
SECRET_KEY=minio123 MINT_MODE="full" /mint/entrypoint.sh \
|
||||
aws-sdk-go aws-sdk-java aws-sdk-php aws-sdk-ruby awscli \
|
||||
healthcheck mc minio-dotnet minio-java minio-js \
|
||||
healthcheck mc minio-dotnet minio-js \
|
||||
minio-py s3cmd s3select security
|
||||
rv=$?
|
||||
|
||||
|
||||
@@ -565,7 +565,16 @@ func (a adminAPIHandlers) ListCannedPolicies(w http.ResponseWriter, r *http.Requ
|
||||
return
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(w).Encode(policies); err != nil {
|
||||
var newPolicies = make(map[string]iampolicy.Policy)
|
||||
for name, p := range policies {
|
||||
_, err = json.Marshal(p)
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
continue
|
||||
}
|
||||
newPolicies[name] = p
|
||||
}
|
||||
if err = json.NewEncoder(w).Encode(newPolicies); err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
+124
-132
@@ -982,16 +982,13 @@ func (a adminAPIHandlers) TraceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w.Header().Set(xhttp.ContentType, "text/event-stream")
|
||||
|
||||
doneCh := make(chan struct{})
|
||||
defer close(doneCh)
|
||||
|
||||
// Trace Publisher and peer-trace-client uses nonblocking send and hence does not wait for slow receivers.
|
||||
// Use buffered channel to take care of burst sends or slow w.Write()
|
||||
traceCh := make(chan interface{}, 4000)
|
||||
|
||||
peers := getRestClients(globalEndpoints)
|
||||
peers := newPeerRestClients(globalEndpoints)
|
||||
|
||||
globalHTTPTrace.Subscribe(traceCh, doneCh, func(entry interface{}) bool {
|
||||
globalHTTPTrace.Subscribe(traceCh, ctx.Done(), func(entry interface{}) bool {
|
||||
return mustTrace(entry, trcAll, trcErr)
|
||||
})
|
||||
|
||||
@@ -999,7 +996,7 @@ func (a adminAPIHandlers) TraceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if peer == nil {
|
||||
continue
|
||||
}
|
||||
peer.Trace(traceCh, doneCh, trcAll, trcErr)
|
||||
peer.Trace(traceCh, ctx.Done(), trcAll, trcErr)
|
||||
}
|
||||
|
||||
keepAliveTicker := time.NewTicker(500 * time.Millisecond)
|
||||
@@ -1018,7 +1015,7 @@ func (a adminAPIHandlers) TraceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
w.(http.Flusher).Flush()
|
||||
case <-GlobalServiceDoneCh:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1052,20 +1049,18 @@ func (a adminAPIHandlers) ConsoleLogHandler(w http.ResponseWriter, r *http.Reque
|
||||
w.Header().Add("Connection", "close")
|
||||
w.Header().Set(xhttp.ContentType, "text/event-stream")
|
||||
|
||||
doneCh := make(chan struct{})
|
||||
defer close(doneCh)
|
||||
logCh := make(chan interface{}, 4000)
|
||||
|
||||
peers := getRestClients(globalEndpoints)
|
||||
peers := newPeerRestClients(globalEndpoints)
|
||||
|
||||
globalConsoleSys.Subscribe(logCh, doneCh, node, limitLines, logKind, nil)
|
||||
globalConsoleSys.Subscribe(logCh, ctx.Done(), node, limitLines, logKind, nil)
|
||||
|
||||
for _, peer := range peers {
|
||||
if peer == nil {
|
||||
continue
|
||||
}
|
||||
if node == "" || strings.EqualFold(peer.host.Name, node) {
|
||||
peer.ConsoleLog(logCh, doneCh)
|
||||
peer.ConsoleLog(logCh, ctx.Done())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1089,7 +1084,7 @@ func (a adminAPIHandlers) ConsoleLogHandler(w http.ResponseWriter, r *http.Reque
|
||||
return
|
||||
}
|
||||
w.(http.Flusher).Flush()
|
||||
case <-GlobalServiceDoneCh:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1175,65 +1170,39 @@ func (a adminAPIHandlers) OBDInfoHandler(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
pulse := make(chan struct{})
|
||||
obdDone := make(chan struct{})
|
||||
obdInfo := madmin.OBDInfo{}
|
||||
obdInfoCh := make(chan madmin.OBDInfo)
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
doPartialWrite := func() {
|
||||
logger.LogIf(ctx, enc.Encode(obdInfo))
|
||||
partialWrite := func(oinfo madmin.OBDInfo) {
|
||||
obdInfoCh <- oinfo
|
||||
}
|
||||
|
||||
partialWrite := func() {
|
||||
pulse <- struct{}{}
|
||||
}
|
||||
|
||||
finish := func() {
|
||||
obdDone <- struct{}{}
|
||||
}
|
||||
setCommonHeaders(w)
|
||||
w.Header().Set(xhttp.ContentType, "text/event-stream")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
errResp := func(err error) {
|
||||
errorResponse := getAPIErrorResponse(ctx, toAdminAPIErr(ctx, err), r.URL.String(),
|
||||
w.Header().Get(xhttp.AmzRequestID), globalDeploymentID)
|
||||
encodedErrorResponse := encodeResponse(errorResponse)
|
||||
obdInfo.Error = string(encodedErrorResponse)
|
||||
finish()
|
||||
logger.LogIf(ctx, enc.Encode(obdInfo))
|
||||
}
|
||||
|
||||
deadline := 3600 * time.Second
|
||||
deadlineStr := r.URL.Query().Get("deadline")
|
||||
if deadlineStr != "" {
|
||||
if dstr := r.URL.Query().Get("deadline"); dstr != "" {
|
||||
var err error
|
||||
deadline, err = time.ParseDuration(deadlineStr)
|
||||
deadline, err = time.ParseDuration(dstr)
|
||||
if err != nil {
|
||||
errResp(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
deadlinedCtx, cancel := context.WithDeadline(ctx, time.Now().Add(deadline))
|
||||
|
||||
setCommonHeaders(w)
|
||||
w.Header().Set(xhttp.ContentType, string(mimeJSON))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
deadlinedCtx, cancel := context.WithTimeout(ctx, deadline)
|
||||
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
go func() {
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
doPartialWrite()
|
||||
case <-pulse:
|
||||
doPartialWrite()
|
||||
case <-obdDone:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
w.(http.Flusher).Flush()
|
||||
cancel()
|
||||
}()
|
||||
defer cancel()
|
||||
|
||||
nsLock := objectAPI.NewNSLock(deadlinedCtx, minioMetaBucket, "obd-in-progress")
|
||||
if err := nsLock.GetLock(newDynamicTimeout(deadline, deadline)); err != nil { // returns a locked lock
|
||||
@@ -1242,89 +1211,114 @@ func (a adminAPIHandlers) OBDInfoHandler(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
defer nsLock.Unlock()
|
||||
|
||||
if cpu, ok := vars["syscpu"]; ok && cpu == "true" {
|
||||
cpuInfo := getLocalCPUOBDInfo(deadlinedCtx)
|
||||
go func() {
|
||||
defer close(obdInfoCh)
|
||||
|
||||
obdInfo.Sys.CPUInfo = append(obdInfo.Sys.CPUInfo, cpuInfo)
|
||||
obdInfo.Sys.CPUInfo = append(obdInfo.Sys.CPUInfo, globalNotificationSys.CPUOBDInfo(deadlinedCtx)...)
|
||||
partialWrite()
|
||||
}
|
||||
if cpu, ok := vars["syscpu"]; ok && cpu == "true" {
|
||||
cpuInfo := getLocalCPUOBDInfo(deadlinedCtx)
|
||||
|
||||
if diskHw, ok := vars["sysdiskhw"]; ok && diskHw == "true" {
|
||||
diskHwInfo := getLocalDiskHwOBD(deadlinedCtx)
|
||||
|
||||
obdInfo.Sys.DiskHwInfo = append(obdInfo.Sys.DiskHwInfo, diskHwInfo)
|
||||
obdInfo.Sys.DiskHwInfo = append(obdInfo.Sys.DiskHwInfo, globalNotificationSys.DiskHwOBDInfo(deadlinedCtx)...)
|
||||
partialWrite()
|
||||
}
|
||||
|
||||
if osInfo, ok := vars["sysosinfo"]; ok && osInfo == "true" {
|
||||
osInfo := getLocalOsInfoOBD(deadlinedCtx)
|
||||
|
||||
obdInfo.Sys.OsInfo = append(obdInfo.Sys.OsInfo, osInfo)
|
||||
obdInfo.Sys.OsInfo = append(obdInfo.Sys.OsInfo, globalNotificationSys.OsOBDInfo(deadlinedCtx)...)
|
||||
partialWrite()
|
||||
}
|
||||
|
||||
if mem, ok := vars["sysmem"]; ok && mem == "true" {
|
||||
memInfo := getLocalMemOBD(deadlinedCtx)
|
||||
|
||||
obdInfo.Sys.MemInfo = append(obdInfo.Sys.MemInfo, memInfo)
|
||||
obdInfo.Sys.MemInfo = append(obdInfo.Sys.MemInfo, globalNotificationSys.MemOBDInfo(deadlinedCtx)...)
|
||||
partialWrite()
|
||||
}
|
||||
|
||||
if proc, ok := vars["sysprocess"]; ok && proc == "true" {
|
||||
procInfo := getLocalProcOBD(deadlinedCtx)
|
||||
|
||||
obdInfo.Sys.ProcInfo = append(obdInfo.Sys.ProcInfo, procInfo)
|
||||
obdInfo.Sys.ProcInfo = append(obdInfo.Sys.ProcInfo, globalNotificationSys.ProcOBDInfo(deadlinedCtx)...)
|
||||
partialWrite()
|
||||
}
|
||||
|
||||
if config, ok := vars["minioconfig"]; ok && config == "true" {
|
||||
cfg, err := readServerConfig(ctx, objectAPI)
|
||||
logger.LogIf(ctx, err)
|
||||
obdInfo.Minio.Config = cfg
|
||||
partialWrite()
|
||||
}
|
||||
|
||||
if drive, ok := vars["perfdrive"]; ok && drive == "true" {
|
||||
// Get drive obd details from local server's drive(s)
|
||||
driveOBDSerial := getLocalDrivesOBD(deadlinedCtx, false, globalEndpoints, r)
|
||||
driveOBDParallel := getLocalDrivesOBD(deadlinedCtx, true, globalEndpoints, r)
|
||||
|
||||
errStr := ""
|
||||
if driveOBDSerial.Error != "" {
|
||||
errStr = "serial: " + driveOBDSerial.Error
|
||||
}
|
||||
if driveOBDParallel.Error != "" {
|
||||
errStr = errStr + " parallel: " + driveOBDParallel.Error
|
||||
obdInfo.Sys.CPUInfo = append(obdInfo.Sys.CPUInfo, cpuInfo)
|
||||
obdInfo.Sys.CPUInfo = append(obdInfo.Sys.CPUInfo, globalNotificationSys.CPUOBDInfo(deadlinedCtx)...)
|
||||
partialWrite(obdInfo)
|
||||
}
|
||||
|
||||
driveOBD := madmin.ServerDrivesOBDInfo{
|
||||
Addr: driveOBDSerial.Addr,
|
||||
Serial: driveOBDSerial.Serial,
|
||||
Parallel: driveOBDParallel.Parallel,
|
||||
Error: errStr,
|
||||
if diskHw, ok := vars["sysdiskhw"]; ok && diskHw == "true" {
|
||||
diskHwInfo := getLocalDiskHwOBD(deadlinedCtx)
|
||||
|
||||
obdInfo.Sys.DiskHwInfo = append(obdInfo.Sys.DiskHwInfo, diskHwInfo)
|
||||
obdInfo.Sys.DiskHwInfo = append(obdInfo.Sys.DiskHwInfo, globalNotificationSys.DiskHwOBDInfo(deadlinedCtx)...)
|
||||
partialWrite(obdInfo)
|
||||
}
|
||||
obdInfo.Perf.DriveInfo = append(obdInfo.Perf.DriveInfo, driveOBD)
|
||||
|
||||
// Notify all other MinIO peers to report drive obd numbers
|
||||
driveOBDs := globalNotificationSys.DriveOBDInfo(deadlinedCtx)
|
||||
obdInfo.Perf.DriveInfo = append(obdInfo.Perf.DriveInfo, driveOBDs...)
|
||||
if osInfo, ok := vars["sysosinfo"]; ok && osInfo == "true" {
|
||||
osInfo := getLocalOsInfoOBD(deadlinedCtx)
|
||||
|
||||
partialWrite()
|
||||
obdInfo.Sys.OsInfo = append(obdInfo.Sys.OsInfo, osInfo)
|
||||
obdInfo.Sys.OsInfo = append(obdInfo.Sys.OsInfo, globalNotificationSys.OsOBDInfo(deadlinedCtx)...)
|
||||
partialWrite(obdInfo)
|
||||
}
|
||||
|
||||
if mem, ok := vars["sysmem"]; ok && mem == "true" {
|
||||
memInfo := getLocalMemOBD(deadlinedCtx)
|
||||
|
||||
obdInfo.Sys.MemInfo = append(obdInfo.Sys.MemInfo, memInfo)
|
||||
obdInfo.Sys.MemInfo = append(obdInfo.Sys.MemInfo, globalNotificationSys.MemOBDInfo(deadlinedCtx)...)
|
||||
partialWrite(obdInfo)
|
||||
}
|
||||
|
||||
if proc, ok := vars["sysprocess"]; ok && proc == "true" {
|
||||
procInfo := getLocalProcOBD(deadlinedCtx)
|
||||
|
||||
obdInfo.Sys.ProcInfo = append(obdInfo.Sys.ProcInfo, procInfo)
|
||||
obdInfo.Sys.ProcInfo = append(obdInfo.Sys.ProcInfo, globalNotificationSys.ProcOBDInfo(deadlinedCtx)...)
|
||||
partialWrite(obdInfo)
|
||||
}
|
||||
|
||||
if config, ok := vars["minioconfig"]; ok && config == "true" {
|
||||
cfg, err := readServerConfig(ctx, objectAPI)
|
||||
logger.LogIf(ctx, err)
|
||||
obdInfo.Minio.Config = cfg
|
||||
partialWrite(obdInfo)
|
||||
}
|
||||
|
||||
if drive, ok := vars["perfdrive"]; ok && drive == "true" {
|
||||
// Get drive obd details from local server's drive(s)
|
||||
driveOBDSerial := getLocalDrivesOBD(deadlinedCtx, false, globalEndpoints, r)
|
||||
driveOBDParallel := getLocalDrivesOBD(deadlinedCtx, true, globalEndpoints, r)
|
||||
|
||||
errStr := ""
|
||||
if driveOBDSerial.Error != "" {
|
||||
errStr = "serial: " + driveOBDSerial.Error
|
||||
}
|
||||
if driveOBDParallel.Error != "" {
|
||||
errStr = errStr + " parallel: " + driveOBDParallel.Error
|
||||
}
|
||||
|
||||
driveOBD := madmin.ServerDrivesOBDInfo{
|
||||
Addr: driveOBDSerial.Addr,
|
||||
Serial: driveOBDSerial.Serial,
|
||||
Parallel: driveOBDParallel.Parallel,
|
||||
Error: errStr,
|
||||
}
|
||||
obdInfo.Perf.DriveInfo = append(obdInfo.Perf.DriveInfo, driveOBD)
|
||||
|
||||
// Notify all other MinIO peers to report drive obd numbers
|
||||
driveOBDs := globalNotificationSys.DriveOBDInfo(deadlinedCtx)
|
||||
obdInfo.Perf.DriveInfo = append(obdInfo.Perf.DriveInfo, driveOBDs...)
|
||||
|
||||
partialWrite(obdInfo)
|
||||
}
|
||||
|
||||
if net, ok := vars["perfnet"]; ok && net == "true" && globalIsDistXL {
|
||||
obdInfo.Perf.Net = append(obdInfo.Perf.Net, globalNotificationSys.NetOBDInfo(deadlinedCtx))
|
||||
obdInfo.Perf.Net = append(obdInfo.Perf.Net, globalNotificationSys.DispatchNetOBDInfo(deadlinedCtx)...)
|
||||
obdInfo.Perf.NetParallel = globalNotificationSys.NetOBDParallelInfo(deadlinedCtx)
|
||||
partialWrite(obdInfo)
|
||||
}
|
||||
}()
|
||||
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case oinfo, ok := <-obdInfoCh:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
logger.LogIf(ctx, enc.Encode(oinfo))
|
||||
w.(http.Flusher).Flush()
|
||||
case <-ticker.C:
|
||||
if _, err := w.Write([]byte(" ")); err != nil {
|
||||
return
|
||||
}
|
||||
w.(http.Flusher).Flush()
|
||||
case <-deadlinedCtx.Done():
|
||||
w.(http.Flusher).Flush()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if net, ok := vars["perfnet"]; ok && net == "true" && globalIsDistXL {
|
||||
obdInfo.Perf.Net = append(obdInfo.Perf.Net, globalNotificationSys.NetOBDInfo(deadlinedCtx))
|
||||
obdInfo.Perf.Net = append(obdInfo.Perf.Net, globalNotificationSys.DispatchNetOBDInfo(deadlinedCtx)...)
|
||||
obdInfo.Perf.NetParallel = globalNotificationSys.NetOBDParallelInfo(deadlinedCtx)
|
||||
partialWrite()
|
||||
}
|
||||
|
||||
finish()
|
||||
}
|
||||
|
||||
// ServerInfoHandler - GET /minio/admin/v3/info
|
||||
@@ -1463,7 +1457,7 @@ func (a adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *http.Reque
|
||||
Mode: mode,
|
||||
Domain: domain,
|
||||
Region: globalServerRegion,
|
||||
SQSARN: globalNotificationSys.GetARNList(),
|
||||
SQSARN: globalNotificationSys.GetARNList(false),
|
||||
DeploymentID: globalDeploymentID,
|
||||
Buckets: buckets,
|
||||
Objects: objects,
|
||||
@@ -1488,7 +1482,9 @@ func (a adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *http.Reque
|
||||
func fetchLambdaInfo(cfg config.Config) []map[string][]madmin.TargetIDStatus {
|
||||
|
||||
// Fetch the configured targets
|
||||
targetList, err := notify.FetchRegisteredTargets(cfg, GlobalServiceDoneCh, NewGatewayHTTPTransport(), true, false)
|
||||
tr := NewGatewayHTTPTransport()
|
||||
defer tr.CloseIdleConnections()
|
||||
targetList, err := notify.FetchRegisteredTargets(cfg, GlobalContext.Done(), tr, true, false)
|
||||
if err != nil && err != notify.ErrTargetsOffline {
|
||||
logger.LogIf(GlobalContext, err)
|
||||
return nil
|
||||
@@ -1611,12 +1607,8 @@ func checkConnection(endpointStr string, timeout time.Duration) error {
|
||||
return pErr
|
||||
}
|
||||
|
||||
tr := newCustomHTTPTransport(
|
||||
&tls.Config{RootCAs: globalRootCAs},
|
||||
timeout,
|
||||
0, /* Default value */
|
||||
)()
|
||||
|
||||
tr := newCustomHTTPTransport(&tls.Config{RootCAs: globalRootCAs}, timeout)()
|
||||
defer tr.CloseIdleConnections()
|
||||
if dErr := u.DialHTTP(tr); dErr != nil {
|
||||
if urlErr, ok := dErr.(*url.Error); ok {
|
||||
// To treat "connection refused" errors as un reachable endpoint.
|
||||
|
||||
@@ -18,6 +18,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -42,7 +43,8 @@ type adminXLTestBed struct {
|
||||
|
||||
// prepareAdminXLTestBed - helper function that setups a single-node
|
||||
// XL backend for admin-handler tests.
|
||||
func prepareAdminXLTestBed() (*adminXLTestBed, error) {
|
||||
func prepareAdminXLTestBed(ctx context.Context) (*adminXLTestBed, error) {
|
||||
|
||||
// reset global variables to start afresh.
|
||||
resetTestGlobals()
|
||||
|
||||
@@ -51,7 +53,7 @@ func prepareAdminXLTestBed() (*adminXLTestBed, error) {
|
||||
globalIsXL = true
|
||||
|
||||
// Initializing objectLayer for HealFormatHandler.
|
||||
objLayer, xlDirs, xlErr := initTestXLObjLayer()
|
||||
objLayer, xlDirs, xlErr := initTestXLObjLayer(ctx)
|
||||
if xlErr != nil {
|
||||
return nil, xlErr
|
||||
}
|
||||
@@ -69,9 +71,9 @@ func prepareAdminXLTestBed() (*adminXLTestBed, error) {
|
||||
globalConfigSys = NewConfigSys()
|
||||
|
||||
globalIAMSys = NewIAMSys()
|
||||
globalIAMSys.Init(GlobalContext, objLayer)
|
||||
globalIAMSys.Init(ctx, objLayer)
|
||||
|
||||
buckets, err := objLayer.ListBuckets(GlobalContext)
|
||||
buckets, err := objLayer.ListBuckets(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -102,7 +104,7 @@ func (atb *adminXLTestBed) TearDown() {
|
||||
|
||||
// initTestObjLayer - Helper function to initialize an XL-based object
|
||||
// layer and set globalObjectAPI.
|
||||
func initTestXLObjLayer() (ObjectLayer, []string, error) {
|
||||
func initTestXLObjLayer(ctx context.Context) (ObjectLayer, []string, error) {
|
||||
xlDirs, err := getRandomDisks(16)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -115,7 +117,7 @@ func initTestXLObjLayer() (ObjectLayer, []string, error) {
|
||||
}
|
||||
|
||||
globalPolicySys = NewPolicySys()
|
||||
objLayer, err := newXLSets(endpoints, storageDisks, format, 1, 16)
|
||||
objLayer, err := newXLSets(ctx, endpoints, storageDisks, format, 1, 16)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -190,7 +192,10 @@ func getServiceCmdRequest(cmd cmdType, cred auth.Credentials) (*http.Request, er
|
||||
// testServicesCmdHandler - parametrizes service subcommand tests on
|
||||
// cmdType value.
|
||||
func testServicesCmdHandler(cmd cmdType, t *testing.T) {
|
||||
adminTestBed, err := prepareAdminXLTestBed()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
adminTestBed, err := prepareAdminXLTestBed(ctx)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to initialize a single node XL backend for admin handler tests.")
|
||||
}
|
||||
@@ -258,7 +263,10 @@ func buildAdminRequest(queryVal url.Values, method, path string,
|
||||
}
|
||||
|
||||
func TestAdminServerInfo(t *testing.T) {
|
||||
adminTestBed, err := prepareAdminXLTestBed()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
adminTestBed, err := prepareAdminXLTestBed(ctx)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to initialize a single node XL backend for admin handler tests.")
|
||||
}
|
||||
|
||||
+8
-2
@@ -88,13 +88,19 @@ func registerAdminRouter(router *mux.Router, enableConfigOps, enableIAMOps bool)
|
||||
adminRouter.Methods(http.MethodGet).Path(adminVersion+"/get-config-kv").HandlerFunc(httpTraceHdrs(adminAPI.GetConfigKVHandler)).Queries("key", "{key:.*}")
|
||||
adminRouter.Methods(http.MethodPut).Path(adminVersion + "/set-config-kv").HandlerFunc(httpTraceHdrs(adminAPI.SetConfigKVHandler))
|
||||
adminRouter.Methods(http.MethodDelete).Path(adminVersion + "/del-config-kv").HandlerFunc(httpTraceHdrs(adminAPI.DelConfigKVHandler))
|
||||
adminRouter.Methods(http.MethodGet).Path(adminVersion+"/help-config-kv").HandlerFunc(httpTraceAll(adminAPI.HelpConfigKVHandler)).Queries("subSys", "{subSys:.*}", "key", "{key:.*}")
|
||||
}
|
||||
|
||||
// Enable config help in all modes.
|
||||
adminRouter.Methods(http.MethodGet).Path(adminVersion+"/help-config-kv").HandlerFunc(httpTraceAll(adminAPI.HelpConfigKVHandler)).Queries("subSys", "{subSys:.*}", "key", "{key:.*}")
|
||||
|
||||
// Config KV history operations.
|
||||
if enableConfigOps {
|
||||
adminRouter.Methods(http.MethodGet).Path(adminVersion+"/list-config-history-kv").HandlerFunc(httpTraceAll(adminAPI.ListConfigHistoryKVHandler)).Queries("count", "{count:[0-9]+}")
|
||||
adminRouter.Methods(http.MethodDelete).Path(adminVersion+"/clear-config-history-kv").HandlerFunc(httpTraceHdrs(adminAPI.ClearConfigHistoryKVHandler)).Queries("restoreId", "{restoreId:.*}")
|
||||
adminRouter.Methods(http.MethodPut).Path(adminVersion+"/restore-config-history-kv").HandlerFunc(httpTraceHdrs(adminAPI.RestoreConfigHistoryKVHandler)).Queries("restoreId", "{restoreId:.*}")
|
||||
}
|
||||
|
||||
/// Config operations
|
||||
/// Config import/export bulk operations
|
||||
if enableConfigOps {
|
||||
// Get config
|
||||
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/config").HandlerFunc(httpTraceHdrs(adminAPI.GetConfigHandler))
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-storage-blob-go/azblob"
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
"google.golang.org/api/googleapi"
|
||||
|
||||
minio "github.com/minio/minio-go/v6"
|
||||
@@ -1917,12 +1916,6 @@ func toAPIError(ctx context.Context, err error) APIError {
|
||||
Description: e.Error(),
|
||||
HTTPStatusCode: e.Response().StatusCode,
|
||||
}
|
||||
case oss.ServiceError:
|
||||
apiErr = APIError{
|
||||
Code: e.Code,
|
||||
Description: e.Message,
|
||||
HTTPStatusCode: e.StatusCode,
|
||||
}
|
||||
// Add more Gateway SDKs here if any in future.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, rs *HTTPRangeSp
|
||||
if !objInfo.Expires.IsZero() {
|
||||
w.Header().Set(xhttp.Expires, objInfo.Expires.UTC().Format(http.TimeFormat))
|
||||
}
|
||||
|
||||
if globalCacheConfig.Enabled {
|
||||
w.Header().Set(xhttp.XCache, objInfo.CacheStatus.String())
|
||||
w.Header().Set(xhttp.XCacheLookup, objInfo.CacheLookupStatus.String())
|
||||
|
||||
@@ -140,6 +140,7 @@ func validateAdminSignature(ctx context.Context, r *http.Request, region string)
|
||||
reqInfo := (&logger.ReqInfo{}).AppendTags("requestHeaders", dumpRequest(r))
|
||||
ctx := logger.SetReqInfo(ctx, reqInfo)
|
||||
logger.LogIf(ctx, errors.New(getAPIError(s3Err).Description), logger.Application)
|
||||
return cred, nil, owner, s3Err
|
||||
}
|
||||
|
||||
claims, s3Err := checkClaimsFromToken(r, cred)
|
||||
|
||||
@@ -391,6 +391,7 @@ func TestIsReqAuthenticated(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckAdminRequestAuthType(t *testing.T) {
|
||||
objLayer, fsDir, err := prepareFS()
|
||||
if err != nil {
|
||||
@@ -425,3 +426,48 @@ func TestCheckAdminRequestAuthType(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAdminSignature(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
objLayer, fsDir, err := prepareFS()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(fsDir)
|
||||
|
||||
if err = newTestConfig(globalMinioDefaultRegion, objLayer); err != nil {
|
||||
t.Fatalf("unable initialize config file, %s", err)
|
||||
}
|
||||
|
||||
creds, err := auth.CreateCredentials("admin", "mypassword")
|
||||
if err != nil {
|
||||
t.Fatalf("unable create credential, %s", err)
|
||||
}
|
||||
globalActiveCred = creds
|
||||
|
||||
testCases := []struct {
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
ErrCode APIErrorCode
|
||||
}{
|
||||
{"", "", ErrInvalidAccessKeyID},
|
||||
{"admin", "", ErrSignatureDoesNotMatch},
|
||||
{"admin", "wrongpassword", ErrSignatureDoesNotMatch},
|
||||
{"wronguser", "mypassword", ErrInvalidAccessKeyID},
|
||||
{"", "mypassword", ErrInvalidAccessKeyID},
|
||||
{"admin", "mypassword", ErrNone},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
req := mustNewRequest("GET", "http://localhost:9000/", 0, nil, t)
|
||||
if err := signRequestV4(req, testCase.AccessKey, testCase.SecretKey); err != nil {
|
||||
t.Fatalf("Unable to inititalized new signed http request %s", err)
|
||||
}
|
||||
_, _, _, s3Error := validateAdminSignature(ctx, req, globalMinioDefaultRegion)
|
||||
if s3Error != testCase.ErrCode {
|
||||
t.Errorf("Test %d: Unexpected s3error returned wanted %d, got %d", i+1, testCase.ErrCode, s3Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-10
@@ -28,11 +28,6 @@ import (
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
)
|
||||
|
||||
// Prepare XL/FS backend for benchmark.
|
||||
func prepareBenchmarkBackend(instanceType string) (ObjectLayer, []string, error) {
|
||||
return prepareTestBackend(instanceType)
|
||||
}
|
||||
|
||||
// Benchmark utility functions for ObjectLayer.PutObject().
|
||||
// Creates Object layer setup ( MakeBucket ) and then runs the PutObject benchmark.
|
||||
func runPutObjectBenchmark(b *testing.B, obj ObjectLayer, objSize int) {
|
||||
@@ -135,7 +130,9 @@ func runPutObjectPartBenchmark(b *testing.B, obj ObjectLayer, partSize int) {
|
||||
// creates XL/FS backend setup, obtains the object layer and calls the runPutObjectPartBenchmark function.
|
||||
func benchmarkPutObjectPart(b *testing.B, instanceType string, objSize int) {
|
||||
// create a temp XL/FS backend.
|
||||
objLayer, disks, err := prepareBenchmarkBackend(instanceType)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
objLayer, disks, err := prepareTestBackend(ctx, instanceType)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed obtaining Temp Backend: <ERROR> %s", err)
|
||||
}
|
||||
@@ -149,7 +146,9 @@ func benchmarkPutObjectPart(b *testing.B, instanceType string, objSize int) {
|
||||
// creates XL/FS backend setup, obtains the object layer and calls the runPutObjectBenchmark function.
|
||||
func benchmarkPutObject(b *testing.B, instanceType string, objSize int) {
|
||||
// create a temp XL/FS backend.
|
||||
objLayer, disks, err := prepareBenchmarkBackend(instanceType)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
objLayer, disks, err := prepareTestBackend(ctx, instanceType)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed obtaining Temp Backend: <ERROR> %s", err)
|
||||
}
|
||||
@@ -163,7 +162,9 @@ func benchmarkPutObject(b *testing.B, instanceType string, objSize int) {
|
||||
// creates XL/FS backend setup, obtains the object layer and runs parallel benchmark for put object.
|
||||
func benchmarkPutObjectParallel(b *testing.B, instanceType string, objSize int) {
|
||||
// create a temp XL/FS backend.
|
||||
objLayer, disks, err := prepareBenchmarkBackend(instanceType)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
objLayer, disks, err := prepareTestBackend(ctx, instanceType)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed obtaining Temp Backend: <ERROR> %s", err)
|
||||
}
|
||||
@@ -242,7 +243,9 @@ func generateBytesData(size int) []byte {
|
||||
// creates XL/FS backend setup, obtains the object layer and calls the runGetObjectBenchmark function.
|
||||
func benchmarkGetObject(b *testing.B, instanceType string, objSize int) {
|
||||
// create a temp XL/FS backend.
|
||||
objLayer, disks, err := prepareBenchmarkBackend(instanceType)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
objLayer, disks, err := prepareTestBackend(ctx, instanceType)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed obtaining Temp Backend: <ERROR> %s", err)
|
||||
}
|
||||
@@ -256,7 +259,9 @@ func benchmarkGetObject(b *testing.B, instanceType string, objSize int) {
|
||||
// creates XL/FS backend setup, obtains the object layer and runs parallel benchmark for ObjectLayer.GetObject() .
|
||||
func benchmarkGetObjectParallel(b *testing.B, instanceType string, objSize int) {
|
||||
// create a temp XL/FS backend.
|
||||
objLayer, disks, err := prepareBenchmarkBackend(instanceType)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
objLayer, disks, err := prepareTestBackend(ctx, instanceType)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed obtaining Temp Backend: <ERROR> %s", err)
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ func newBootstrapRESTClient(endpoint Endpoint) (*bootstrapRESTClient, error) {
|
||||
}
|
||||
}
|
||||
|
||||
trFn := newCustomHTTPTransport(tlsConfig, rest.DefaultRESTTimeout, rest.DefaultRESTTimeout)
|
||||
trFn := newCustomHTTPTransport(tlsConfig, rest.DefaultRESTTimeout)
|
||||
restClient, err := rest.NewClient(serverURL, trFn, newAuthToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -282,16 +282,13 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
|
||||
|
||||
w.Header().Set(xhttp.ContentType, "text/event-stream")
|
||||
|
||||
doneCh := make(chan struct{})
|
||||
defer close(doneCh)
|
||||
|
||||
// Listen Publisher and peer-listen-client uses nonblocking send and hence does not wait for slow receivers.
|
||||
// Use buffered channel to take care of burst sends or slow w.Write()
|
||||
listenCh := make(chan interface{}, 4000)
|
||||
|
||||
peers := getRestClients(globalEndpoints)
|
||||
peers := newPeerRestClients(globalEndpoints)
|
||||
|
||||
globalHTTPListen.Subscribe(listenCh, doneCh, func(evI interface{}) bool {
|
||||
globalHTTPListen.Subscribe(listenCh, ctx.Done(), func(evI interface{}) bool {
|
||||
ev, ok := evI.(event.Event)
|
||||
if !ok {
|
||||
return false
|
||||
@@ -310,7 +307,7 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
|
||||
if peer == nil {
|
||||
continue
|
||||
}
|
||||
peer.Listen(listenCh, doneCh, values)
|
||||
peer.Listen(listenCh, ctx.Done(), values)
|
||||
}
|
||||
|
||||
keepAliveTicker := time.NewTicker(500 * time.Millisecond)
|
||||
@@ -336,7 +333,7 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
|
||||
return
|
||||
}
|
||||
w.(http.Flusher).Flush()
|
||||
case <-GlobalServiceDoneCh:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,11 @@ func readConfig(ctx context.Context, objAPI ObjectLayer, configFile string) ([]b
|
||||
}
|
||||
|
||||
func deleteConfig(ctx context.Context, objAPI ObjectLayer, configFile string) error {
|
||||
return objAPI.DeleteObject(ctx, minioMetaBucket, configFile)
|
||||
err := objAPI.DeleteObject(ctx, minioMetaBucket, configFile)
|
||||
if err != nil && isErrObjectNotFound(err) {
|
||||
return errConfigNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func saveConfig(ctx context.Context, objAPI ObjectLayer, configFile string, data []byte) error {
|
||||
|
||||
@@ -307,7 +307,7 @@ func validateConfig(s config.Config) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return notify.TestNotificationTargets(s, GlobalServiceDoneCh, NewGatewayHTTPTransport(),
|
||||
return notify.TestNotificationTargets(s, GlobalContext.Done(), NewGatewayHTTPTransport(),
|
||||
globalNotificationSys.ConfiguredTargetIDs())
|
||||
}
|
||||
|
||||
@@ -469,12 +469,12 @@ func lookupConfigs(s config.Config) {
|
||||
}
|
||||
}
|
||||
|
||||
globalConfigTargetList, err = notify.GetNotificationTargets(s, GlobalServiceDoneCh, NewGatewayHTTPTransport())
|
||||
globalConfigTargetList, err = notify.GetNotificationTargets(s, GlobalContext.Done(), NewGatewayHTTPTransport())
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, fmt.Errorf("Unable to initialize notification target(s): %w", err))
|
||||
}
|
||||
|
||||
globalEnvTargetList, err = notify.GetNotificationTargets(newServerConfig(), GlobalServiceDoneCh, NewGatewayHTTPTransport())
|
||||
globalEnvTargetList, err = notify.GetNotificationTargets(newServerConfig(), GlobalContext.Done(), NewGatewayHTTPTransport())
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, fmt.Errorf("Unable to initialize notification target(s): %w", err))
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ func handleEncryptedConfigBackend(objAPI ObjectLayer, server bool) error {
|
||||
|
||||
rquorum := InsufficientReadQuorum{}
|
||||
wquorum := InsufficientWriteQuorum{}
|
||||
bucketNotFound := BucketNotFound{}
|
||||
|
||||
for !stop {
|
||||
select {
|
||||
@@ -58,7 +57,7 @@ func handleEncryptedConfigBackend(objAPI ObjectLayer, server bool) error {
|
||||
if encrypted, err = checkBackendEncrypted(objAPI); err != nil {
|
||||
if errors.Is(err, errDiskNotFound) ||
|
||||
errors.As(err, &rquorum) ||
|
||||
errors.As(err, &bucketNotFound) {
|
||||
isErrBucketNotFound(err) {
|
||||
logger.Info("Waiting for config backend to be encrypted..")
|
||||
continue
|
||||
}
|
||||
@@ -108,7 +107,7 @@ func handleEncryptedConfigBackend(objAPI ObjectLayer, server bool) error {
|
||||
if errors.Is(err, errDiskNotFound) ||
|
||||
errors.As(err, &rquorum) ||
|
||||
errors.As(err, &wquorum) ||
|
||||
errors.As(err, &bucketNotFound) {
|
||||
isErrBucketNotFound(err) {
|
||||
logger.Info("Waiting for config backend to be encrypted..")
|
||||
continue
|
||||
}
|
||||
|
||||
+3
-5
@@ -186,16 +186,14 @@ func (sys *ConfigSys) Load(objAPI ObjectLayer) error {
|
||||
}
|
||||
|
||||
// WatchConfigNASDisk - watches nas disk on periodic basis.
|
||||
func (sys *ConfigSys) WatchConfigNASDisk(objAPI ObjectLayer) {
|
||||
func (sys *ConfigSys) WatchConfigNASDisk(ctx context.Context, objAPI ObjectLayer) {
|
||||
configInterval := globalRefreshIAMInterval
|
||||
watchDisk := func() {
|
||||
ticker := time.NewTicker(configInterval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-GlobalServiceDoneCh:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
case <-time.After(configInterval):
|
||||
loadConfig(objAPI)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-29
@@ -22,7 +22,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/minio-go/v6/pkg/set"
|
||||
@@ -580,33 +579,6 @@ func (c Config) Clone() Config {
|
||||
return cp
|
||||
}
|
||||
|
||||
// Converts an input string of form "k1=v1 k2=v2" into fields
|
||||
// of ["k1=v1", "k2=v2"], the tokenization of each `k=v`
|
||||
// happens with the right number of input keys, if keys
|
||||
// input is empty returned value is empty slice as well.
|
||||
func kvFields(input string, keys []string) []string {
|
||||
var valueIndexes []int
|
||||
for _, key := range keys {
|
||||
i := strings.Index(input, key+KvSeparator)
|
||||
if i == -1 {
|
||||
continue
|
||||
}
|
||||
valueIndexes = append(valueIndexes, i)
|
||||
}
|
||||
|
||||
sort.Ints(valueIndexes)
|
||||
var fields = make([]string, len(valueIndexes))
|
||||
for i := range valueIndexes {
|
||||
j := i + 1
|
||||
if j < len(valueIndexes) {
|
||||
fields[i] = strings.TrimSpace(input[valueIndexes[i]:valueIndexes[j]])
|
||||
} else {
|
||||
fields[i] = strings.TrimSpace(input[valueIndexes[i]:])
|
||||
}
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
// SetKVS - set specific key values per sub-system.
|
||||
func (c Config) SetKVS(s string, defaultKVS map[string]KVS) error {
|
||||
if len(s) == 0 {
|
||||
@@ -635,7 +607,7 @@ func (c Config) SetKVS(s string, defaultKVS map[string]KVS) error {
|
||||
tgt = subSystemValue[1]
|
||||
}
|
||||
|
||||
fields := kvFields(inputs[1], defaultKVS[subSys].Keys())
|
||||
fields := madmin.KvFields(inputs[1], defaultKVS[subSys].Keys())
|
||||
if len(fields) == 0 {
|
||||
return Errorf("sub-system '%s' cannot have empty keys", subSys)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/minio/minio/pkg/madmin"
|
||||
)
|
||||
|
||||
func TestKVFields(t *testing.T) {
|
||||
@@ -90,7 +92,7 @@ func TestKVFields(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run("", func(t *testing.T) {
|
||||
gotFields := kvFields(test.input, test.keys)
|
||||
gotFields := madmin.KvFields(test.input, test.keys)
|
||||
if len(gotFields) != len(test.expectedFields) {
|
||||
t.Errorf("Expected keys %d, found %d", len(test.expectedFields), len(gotFields))
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
const (
|
||||
formatComment = `'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'`
|
||||
queueDirComment = `staging dir for undelivered messages e.g. '/home/events'`
|
||||
queueLimitComment = `maximum limit for undelivered messages, defaults to '10000'`
|
||||
queueLimitComment = `maximum limit for undelivered messages, defaults to '100000'`
|
||||
)
|
||||
|
||||
// Help template inputs for all notification targets
|
||||
|
||||
@@ -102,8 +102,10 @@ func FetchRegisteredTargets(cfg config.Config, doneCh <-chan struct{}, transport
|
||||
var targetsOffline bool
|
||||
|
||||
defer func() {
|
||||
// Automatically close all connections to targets when an error occur
|
||||
if err != nil {
|
||||
// Automatically close all connections to targets when an error occur.
|
||||
// Close all the targets if returnOnTargetError is set
|
||||
// Else, close only the failed targets
|
||||
if err != nil && returnOnTargetError {
|
||||
for _, t := range targetList.TargetMap() {
|
||||
_ = t.Close()
|
||||
}
|
||||
@@ -174,6 +176,7 @@ func FetchRegisteredTargets(cfg config.Config, doneCh <-chan struct{}, transport
|
||||
if returnOnTargetError {
|
||||
return nil, err
|
||||
}
|
||||
_ = newTarget.Close()
|
||||
}
|
||||
|
||||
if err = targetList.Add(newTarget); err != nil {
|
||||
@@ -194,6 +197,7 @@ func FetchRegisteredTargets(cfg config.Config, doneCh <-chan struct{}, transport
|
||||
if returnOnTargetError {
|
||||
return nil, err
|
||||
}
|
||||
_ = newTarget.Close()
|
||||
}
|
||||
if err = targetList.Add(newTarget); err != nil {
|
||||
logger.LogIf(context.Background(), err)
|
||||
@@ -214,6 +218,7 @@ func FetchRegisteredTargets(cfg config.Config, doneCh <-chan struct{}, transport
|
||||
if returnOnTargetError {
|
||||
return nil, err
|
||||
}
|
||||
_ = newTarget.Close()
|
||||
}
|
||||
if err = targetList.Add(newTarget); err != nil {
|
||||
logger.LogIf(context.Background(), err)
|
||||
@@ -234,6 +239,7 @@ func FetchRegisteredTargets(cfg config.Config, doneCh <-chan struct{}, transport
|
||||
if returnOnTargetError {
|
||||
return nil, err
|
||||
}
|
||||
_ = newTarget.Close()
|
||||
}
|
||||
if err = targetList.Add(newTarget); err != nil {
|
||||
logger.LogIf(context.Background(), err)
|
||||
@@ -253,6 +259,7 @@ func FetchRegisteredTargets(cfg config.Config, doneCh <-chan struct{}, transport
|
||||
if returnOnTargetError {
|
||||
return nil, err
|
||||
}
|
||||
_ = newTarget.Close()
|
||||
}
|
||||
if err = targetList.Add(newTarget); err != nil {
|
||||
logger.LogIf(context.Background(), err)
|
||||
@@ -272,6 +279,7 @@ func FetchRegisteredTargets(cfg config.Config, doneCh <-chan struct{}, transport
|
||||
if returnOnTargetError {
|
||||
return nil, err
|
||||
}
|
||||
_ = newTarget.Close()
|
||||
}
|
||||
if err = targetList.Add(newTarget); err != nil {
|
||||
logger.LogIf(context.Background(), err)
|
||||
@@ -291,6 +299,7 @@ func FetchRegisteredTargets(cfg config.Config, doneCh <-chan struct{}, transport
|
||||
if returnOnTargetError {
|
||||
return nil, err
|
||||
}
|
||||
_ = newTarget.Close()
|
||||
}
|
||||
if err = targetList.Add(newTarget); err != nil {
|
||||
logger.LogIf(context.Background(), err)
|
||||
@@ -310,6 +319,7 @@ func FetchRegisteredTargets(cfg config.Config, doneCh <-chan struct{}, transport
|
||||
if returnOnTargetError {
|
||||
return nil, err
|
||||
}
|
||||
_ = newTarget.Close()
|
||||
}
|
||||
if err = targetList.Add(newTarget); err != nil {
|
||||
logger.LogIf(context.Background(), err)
|
||||
@@ -329,6 +339,7 @@ func FetchRegisteredTargets(cfg config.Config, doneCh <-chan struct{}, transport
|
||||
if returnOnTargetError {
|
||||
return nil, err
|
||||
}
|
||||
_ = newTarget.Close()
|
||||
}
|
||||
if err = targetList.Add(newTarget); err != nil {
|
||||
logger.LogIf(context.Background(), err)
|
||||
@@ -348,6 +359,7 @@ func FetchRegisteredTargets(cfg config.Config, doneCh <-chan struct{}, transport
|
||||
if returnOnTargetError {
|
||||
return nil, err
|
||||
}
|
||||
_ = newTarget.Close()
|
||||
}
|
||||
if err = targetList.Add(newTarget); err != nil {
|
||||
logger.LogIf(context.Background(), err)
|
||||
|
||||
@@ -74,7 +74,7 @@ func (sys *HTTPConsoleLoggerSys) HasLogListeners() bool {
|
||||
}
|
||||
|
||||
// Subscribe starts console logging for this node.
|
||||
func (sys *HTTPConsoleLoggerSys) Subscribe(subCh chan interface{}, doneCh chan struct{}, node string, last int, logKind string, filter func(entry interface{}) bool) {
|
||||
func (sys *HTTPConsoleLoggerSys) Subscribe(subCh chan interface{}, doneCh <-chan struct{}, node string, last int, logKind string, filter func(entry interface{}) bool) {
|
||||
// Enable console logging for remote client.
|
||||
if !sys.HasLogListeners() {
|
||||
logger.AddTarget(sys)
|
||||
|
||||
@@ -847,11 +847,11 @@ func (c *diskCache) Get(ctx context.Context, bucket, object string, rs *HTTPRang
|
||||
if HasSuffix(object, SlashSeparator) {
|
||||
// The lock taken above is released when
|
||||
// objReader.Close() is called by the caller.
|
||||
gr, gerr := NewGetObjectReaderFromReader(bytes.NewBuffer(nil), objInfo, opts.CheckCopyPrecondFn, nsUnlocker)
|
||||
gr, gerr := NewGetObjectReaderFromReader(bytes.NewBuffer(nil), objInfo, opts, nsUnlocker)
|
||||
return gr, numHits, gerr
|
||||
}
|
||||
|
||||
fn, off, length, nErr := NewGetObjectReader(rs, objInfo, opts.CheckCopyPrecondFn, nsUnlocker)
|
||||
fn, off, length, nErr := NewGetObjectReader(rs, objInfo, opts, nsUnlocker)
|
||||
if nErr != nil {
|
||||
return nil, numHits, nErr
|
||||
}
|
||||
|
||||
+11
-2
@@ -207,6 +207,7 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string
|
||||
return cacheReader, nil
|
||||
}
|
||||
if cc != nil && cc.noStore {
|
||||
cacheReader.Close()
|
||||
c.cacheStats.incMiss()
|
||||
bReader, err := c.GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts)
|
||||
bReader.ObjInfo.CacheLookupStatus = CacheHit
|
||||
@@ -220,9 +221,11 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string
|
||||
c.incCacheStats(cacheObjSize)
|
||||
return cacheReader, nil
|
||||
} else if err != nil {
|
||||
if cacheErr == nil {
|
||||
cacheReader.Close()
|
||||
}
|
||||
if _, ok := err.(ObjectNotFound); ok {
|
||||
if cacheErr == nil {
|
||||
cacheReader.Close()
|
||||
// Delete cached entry if backend object
|
||||
// was deleted.
|
||||
dcache.Delete(ctx, bucket, object)
|
||||
@@ -233,6 +236,9 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string
|
||||
}
|
||||
|
||||
if !objInfo.IsCacheable() {
|
||||
if cacheErr == nil {
|
||||
cacheReader.Close()
|
||||
}
|
||||
c.cacheStats.incMiss()
|
||||
return c.GetObjectNInfoFn(ctx, bucket, object, rs, h, lockType, opts)
|
||||
}
|
||||
@@ -240,6 +246,9 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string
|
||||
objRetention := objectlock.GetObjectRetentionMeta(objInfo.UserDefined)
|
||||
legalHold := objectlock.GetObjectLegalHoldMeta(objInfo.UserDefined)
|
||||
if objRetention.Mode.Valid() || legalHold.Status.Valid() {
|
||||
if cacheErr == nil {
|
||||
cacheReader.Close()
|
||||
}
|
||||
c.cacheStats.incMiss()
|
||||
return c.GetObjectNInfoFn(ctx, bucket, object, rs, h, lockType, opts)
|
||||
}
|
||||
@@ -312,7 +321,7 @@ func (c *cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string
|
||||
}()
|
||||
cleanupBackend := func() { bkReader.Close() }
|
||||
cleanupPipe := func() { pipeWriter.Close() }
|
||||
return NewGetObjectReaderFromReader(teeReader, bkReader.ObjInfo, opts.CheckCopyPrecondFn, cleanupBackend, cleanupPipe)
|
||||
return NewGetObjectReaderFromReader(teeReader, bkReader.ObjInfo, opts, cleanupBackend, cleanupPipe)
|
||||
}
|
||||
|
||||
// Returns ObjectInfo from cache if available.
|
||||
|
||||
+21
-2
@@ -982,6 +982,19 @@ func getOpts(ctx context.Context, r *http.Request, bucket, object string) (Objec
|
||||
encryption encrypt.ServerSide
|
||||
opts ObjectOptions
|
||||
)
|
||||
|
||||
var partNumber int
|
||||
var err error
|
||||
if pn := r.URL.Query().Get("partNumber"); pn != "" {
|
||||
partNumber, err = strconv.Atoi(pn)
|
||||
if err != nil {
|
||||
return opts, err
|
||||
}
|
||||
if partNumber < 0 {
|
||||
return opts, errInvalidArgument
|
||||
}
|
||||
}
|
||||
|
||||
if GlobalGatewaySSE.SSEC() && crypto.SSEC.IsRequested(r.Header) {
|
||||
key, err := crypto.SSEC.ParseHTTP(r.Header)
|
||||
if err != nil {
|
||||
@@ -990,10 +1003,16 @@ func getOpts(ctx context.Context, r *http.Request, bucket, object string) (Objec
|
||||
derivedKey := deriveClientKey(key, bucket, object)
|
||||
encryption, err = encrypt.NewSSEC(derivedKey[:])
|
||||
logger.CriticalIf(ctx, err)
|
||||
return ObjectOptions{ServerSideEncryption: encryption}, nil
|
||||
return ObjectOptions{ServerSideEncryption: encryption, PartNumber: partNumber}, nil
|
||||
}
|
||||
|
||||
// default case of passing encryption headers to backend
|
||||
return getDefaultOpts(r.Header, false, nil)
|
||||
opts, err = getDefaultOpts(r.Header, false, nil)
|
||||
if err != nil {
|
||||
return opts, err
|
||||
}
|
||||
opts.PartNumber = partNumber
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// get ObjectOptions for PUT calls from encryption headers and metadata
|
||||
|
||||
@@ -757,13 +757,13 @@ func (fs *FSObjects) AbortMultipartUpload(ctx context.Context, bucket, object, u
|
||||
// Removes multipart uploads if any older than `expiry` duration
|
||||
// on all buckets for every `cleanupInterval`, this function is
|
||||
// blocking and should be run in a go-routine.
|
||||
func (fs *FSObjects) cleanupStaleMultipartUploads(ctx context.Context, cleanupInterval, expiry time.Duration, doneCh <-chan struct{}) {
|
||||
func (fs *FSObjects) cleanupStaleMultipartUploads(ctx context.Context, cleanupInterval, expiry time.Duration) {
|
||||
ticker := time.NewTicker(cleanupInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-doneCh:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
now := time.Now()
|
||||
|
||||
@@ -51,7 +51,7 @@ func TestFSCleanupMultipartUploadsInRoutine(t *testing.T) {
|
||||
cleanupWg.Add(1)
|
||||
go func() {
|
||||
defer cleanupWg.Done()
|
||||
fs.cleanupStaleMultipartUploads(GlobalContext, time.Millisecond, 0, ctx.Done())
|
||||
fs.cleanupStaleMultipartUploads(ctx, time.Millisecond, 0)
|
||||
}()
|
||||
|
||||
// Wait for 100ms such that - we have given enough time for
|
||||
|
||||
+3
-3
@@ -179,7 +179,7 @@ func NewFSObjectLayer(fsPath string) (ObjectLayer, error) {
|
||||
// or cause changes on backend format.
|
||||
fs.fsFormatRlk = rlk
|
||||
|
||||
go fs.cleanupStaleMultipartUploads(ctx, GlobalMultipartCleanupInterval, GlobalMultipartExpiry, GlobalServiceDoneCh)
|
||||
go fs.cleanupStaleMultipartUploads(ctx, GlobalMultipartCleanupInterval, GlobalMultipartExpiry)
|
||||
|
||||
// Return successfully initialized object layer.
|
||||
return fs, nil
|
||||
@@ -561,7 +561,7 @@ func (fs *FSObjects) GetObjectNInfo(ctx context.Context, bucket, object string,
|
||||
if HasSuffix(object, SlashSeparator) {
|
||||
// The lock taken above is released when
|
||||
// objReader.Close() is called by the caller.
|
||||
return NewGetObjectReaderFromReader(bytes.NewBuffer(nil), objInfo, opts.CheckCopyPrecondFn, nsUnlocker)
|
||||
return NewGetObjectReaderFromReader(bytes.NewBuffer(nil), objInfo, opts, nsUnlocker)
|
||||
}
|
||||
// Take a rwPool lock for NFS gateway type deployment
|
||||
rwPoolUnlocker := func() {}
|
||||
@@ -578,7 +578,7 @@ func (fs *FSObjects) GetObjectNInfo(ctx context.Context, bucket, object string,
|
||||
rwPoolUnlocker = func() { fs.rwPool.Close(fsMetaPath) }
|
||||
}
|
||||
|
||||
objReaderFn, off, length, rErr := NewGetObjectReader(rs, objInfo, opts.CheckCopyPrecondFn, nsUnlocker, rwPoolUnlocker)
|
||||
objReaderFn, off, length, rErr := NewGetObjectReader(rs, objInfo, opts, nsUnlocker, rwPoolUnlocker)
|
||||
if rErr != nil {
|
||||
return nil, rErr
|
||||
}
|
||||
|
||||
+6
-1
@@ -17,7 +17,9 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -196,6 +198,9 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
|
||||
|
||||
httpServer := xhttp.NewServer([]string{globalCLIContext.Addr},
|
||||
criticalErrorHandler{registerHandlers(router, globalHandlers...)}, getCert)
|
||||
httpServer.BaseContext = func(listener net.Listener) context.Context {
|
||||
return GlobalContext
|
||||
}
|
||||
go func() {
|
||||
globalHTTPServerErrorCh <- httpServer.Start()
|
||||
}()
|
||||
@@ -244,7 +249,7 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
|
||||
logger.FatalIf(globalNotificationSys.Init(buckets, newObject), "Unable to initialize notification system")
|
||||
// Start watching disk for reloading config, this
|
||||
// is only enabled for "NAS" gateway.
|
||||
globalConfigSys.WatchConfigNASDisk(newObject)
|
||||
globalConfigSys.WatchConfigNASDisk(GlobalContext, newObject)
|
||||
}
|
||||
// This is only to uniquely identify each gateway deployments.
|
||||
globalDeploymentID = env.Get("MINIO_GATEWAY_DEPLOYMENT_ID", mustGetUUID())
|
||||
|
||||
@@ -264,7 +264,6 @@ func s3MetaToAzureProperties(ctx context.Context, s3Metadata map[string]string)
|
||||
// handle it for storage.
|
||||
k = strings.Replace(k, "X-Amz-Meta-", "", 1)
|
||||
blobMeta[encodeKey(k)] = v
|
||||
|
||||
// All cases below, extract common metadata that is
|
||||
// accepted by S3 into BlobProperties for setting on
|
||||
// Azure - see
|
||||
@@ -306,6 +305,28 @@ func newPartMetaV1(uploadID string, partID int) (partMeta *partMetadataV1) {
|
||||
return p
|
||||
}
|
||||
|
||||
func s3StorageClassToAzureTier(sc string) azblob.AccessTierType {
|
||||
switch sc {
|
||||
case "REDUCED_REDUNDANCY":
|
||||
return azblob.AccessTierCool
|
||||
case "STANDARD":
|
||||
return azblob.AccessTierHot
|
||||
}
|
||||
return azblob.AccessTierHot
|
||||
}
|
||||
|
||||
func azureTierToS3StorageClass(tierType string) string {
|
||||
switch azblob.AccessTierType(tierType) {
|
||||
case azblob.AccessTierCool:
|
||||
return "REDUCED_REDUNDANCY"
|
||||
case azblob.AccessTierHot:
|
||||
return "STANDARD"
|
||||
default:
|
||||
return "STANDARD"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// azurePropertiesToS3Meta converts Azure metadata/properties to S3
|
||||
// metadata. It is the reverse of s3MetaToAzureProperties. Azure's
|
||||
// `.GetMetadata()` lower-cases all header keys, so this is taken into
|
||||
@@ -408,6 +429,8 @@ func azureCodesToObjectError(err error, serviceCode string, statusCode int, buck
|
||||
err = minio.PartTooBig{}
|
||||
case "InvalidMetadata":
|
||||
err = minio.UnsupportedMetadata{}
|
||||
case "BlobAccessTierNotSupportedForAccountType":
|
||||
err = minio.NotImplemented{}
|
||||
default:
|
||||
switch statusCode {
|
||||
case http.StatusNotFound:
|
||||
@@ -639,6 +662,7 @@ func (a *azureObjects) ListObjects(ctx context.Context, bucket, prefix, marker,
|
||||
ETag: etag,
|
||||
ContentType: *blob.Properties.ContentType,
|
||||
ContentEncoding: *blob.Properties.ContentEncoding,
|
||||
UserDefined: blob.Metadata,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -718,7 +742,7 @@ func (a *azureObjects) GetObjectNInfo(ctx context.Context, bucket, object string
|
||||
// Setup cleanup function to cause the above go-routine to
|
||||
// exit in case of partial read
|
||||
pipeCloser := func() { pr.Close() }
|
||||
return minio.NewGetObjectReaderFromReader(pr, objInfo, opts.CheckCopyPrecondFn, pipeCloser)
|
||||
return minio.NewGetObjectReaderFromReader(pr, objInfo, opts, pipeCloser)
|
||||
}
|
||||
|
||||
// GetObject - reads an object from azure. Supports additional
|
||||
@@ -786,6 +810,7 @@ func (a *azureObjects) GetObjectInfo(ctx context.Context, bucket, object string,
|
||||
Size: blob.ContentLength(),
|
||||
ContentType: blob.ContentType(),
|
||||
ContentEncoding: blob.ContentEncoding(),
|
||||
StorageClass: azureTierToS3StorageClass(blob.AccessTier()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -855,7 +880,7 @@ func (a *azureObjects) CopyObject(ctx context.Context, srcBucket, srcObject, des
|
||||
// To handle the case where the source object should be copied without its metadata,
|
||||
// the metadata must be removed from the dest. object after the copy completes
|
||||
if len(azureMeta) == 0 {
|
||||
_, err := destBlob.SetMetadata(ctx, azureMeta, azblob.BlobAccessConditions{})
|
||||
_, err = destBlob.SetMetadata(ctx, azureMeta, azblob.BlobAccessConditions{})
|
||||
if err != nil {
|
||||
return objInfo, azureToObjectError(err, srcBucket, srcObject)
|
||||
}
|
||||
@@ -865,6 +890,15 @@ func (a *azureObjects) CopyObject(ctx context.Context, srcBucket, srcObject, des
|
||||
if err != nil {
|
||||
return objInfo, azureToObjectError(err, srcBucket, srcObject)
|
||||
}
|
||||
|
||||
if _, ok := srcInfo.UserDefined["x-amz-storage-class"]; ok {
|
||||
_, err = destBlob.SetTier(ctx, s3StorageClassToAzureTier(srcInfo.UserDefined["x-amz-storage-class"]),
|
||||
azblob.LeaseAccessConditions{})
|
||||
if err != nil {
|
||||
return objInfo, azureToObjectError(err, srcBucket, srcObject)
|
||||
}
|
||||
}
|
||||
|
||||
return a.GetObjectInfo(ctx, destBucket, destObject, dstOpts)
|
||||
}
|
||||
|
||||
|
||||
@@ -478,7 +478,7 @@ func (l *b2Objects) GetObjectNInfo(ctx context.Context, bucket, object string, r
|
||||
// Setup cleanup function to cause the above go-routine to
|
||||
// exit in case of partial read
|
||||
pipeCloser := func() { pr.Close() }
|
||||
return minio.NewGetObjectReaderFromReader(pr, objInfo, opts.CheckCopyPrecondFn, pipeCloser)
|
||||
return minio.NewGetObjectReaderFromReader(pr, objInfo, opts, pipeCloser)
|
||||
}
|
||||
|
||||
// GetObject reads an object from B2. Supports additional
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
_ "github.com/minio/minio/cmd/gateway/gcs"
|
||||
_ "github.com/minio/minio/cmd/gateway/hdfs"
|
||||
_ "github.com/minio/minio/cmd/gateway/nas"
|
||||
_ "github.com/minio/minio/cmd/gateway/oss"
|
||||
_ "github.com/minio/minio/cmd/gateway/s3"
|
||||
|
||||
// B2 is specifically kept here to avoid re-ordering by goimports,
|
||||
|
||||
@@ -754,7 +754,7 @@ func (l *gcsGateway) GetObjectNInfo(ctx context.Context, bucket, object string,
|
||||
// Setup cleanup function to cause the above go-routine to
|
||||
// exit in case of partial read
|
||||
pipeCloser := func() { pr.Close() }
|
||||
return minio.NewGetObjectReaderFromReader(pr, objInfo, opts.CheckCopyPrecondFn, pipeCloser)
|
||||
return minio.NewGetObjectReaderFromReader(pr, objInfo, opts, pipeCloser)
|
||||
}
|
||||
|
||||
// GetObject - reads an object from GCS. Supports additional
|
||||
|
||||
@@ -466,7 +466,7 @@ func (n *hdfsObjects) GetObjectNInfo(ctx context.Context, bucket, object string,
|
||||
// Setup cleanup function to cause the above go-routine to
|
||||
// exit in case of partial read
|
||||
pipeCloser := func() { pr.Close() }
|
||||
return minio.NewGetObjectReaderFromReader(pr, objInfo, opts.CheckCopyPrecondFn, pipeCloser)
|
||||
return minio.NewGetObjectReaderFromReader(pr, objInfo, opts, pipeCloser)
|
||||
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* MinIO Cloud Storage, (C) 2017 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package oss
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
|
||||
minio "github.com/minio/minio/cmd"
|
||||
)
|
||||
|
||||
func ossErrResponse(code string) error {
|
||||
return oss.ServiceError{
|
||||
Code: code,
|
||||
}
|
||||
}
|
||||
|
||||
func TestOSSToObjectError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
inputErr error
|
||||
expectedErr error
|
||||
bucket, object string
|
||||
}{
|
||||
{
|
||||
inputErr: ossErrResponse("BucketAlreadyExists"),
|
||||
expectedErr: minio.BucketAlreadyOwnedByYou{},
|
||||
},
|
||||
{
|
||||
inputErr: ossErrResponse("BucketNotEmpty"),
|
||||
expectedErr: minio.BucketNotEmpty{},
|
||||
},
|
||||
{
|
||||
inputErr: ossErrResponse("InvalidBucketName"),
|
||||
expectedErr: minio.BucketNameInvalid{},
|
||||
},
|
||||
{
|
||||
inputErr: ossErrResponse("NoSuchBucket"),
|
||||
expectedErr: minio.BucketNotFound{},
|
||||
},
|
||||
// with empty object, NoSuchKey is interpreted as BucketNotFound
|
||||
{
|
||||
inputErr: ossErrResponse("NoSuchKey"),
|
||||
expectedErr: minio.BucketNotFound{},
|
||||
},
|
||||
{
|
||||
inputErr: ossErrResponse("NoSuchUpload"),
|
||||
expectedErr: minio.InvalidUploadID{},
|
||||
},
|
||||
{
|
||||
inputErr: ossErrResponse("InvalidObjectName"),
|
||||
expectedErr: minio.ObjectNameInvalid{},
|
||||
},
|
||||
{
|
||||
inputErr: ossErrResponse("AccessDenied"),
|
||||
expectedErr: minio.PrefixAccessDenied{},
|
||||
},
|
||||
{
|
||||
inputErr: ossErrResponse("NoSuchUpload"),
|
||||
expectedErr: minio.InvalidUploadID{},
|
||||
},
|
||||
{
|
||||
inputErr: ossErrResponse("EntityTooSmall"),
|
||||
expectedErr: minio.PartTooSmall{},
|
||||
},
|
||||
{
|
||||
inputErr: nil,
|
||||
expectedErr: nil,
|
||||
},
|
||||
// Special test case for NoSuchKey with object name
|
||||
{
|
||||
inputErr: ossErrResponse("NoSuchKey"),
|
||||
expectedErr: minio.ObjectNotFound{Bucket: "bucket", Object: "object"},
|
||||
bucket: "bucket",
|
||||
object: "object",
|
||||
},
|
||||
|
||||
// Special test case for error value that is not of
|
||||
// type (*Error)
|
||||
{
|
||||
inputErr: fmt.Errorf("not a *Error"),
|
||||
expectedErr: fmt.Errorf("not a *Error"),
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
actualErr := ossToObjectError(tc.inputErr, tc.bucket, tc.object)
|
||||
if actualErr != nil && tc.expectedErr != nil && actualErr.Error() != tc.expectedErr.Error() {
|
||||
t.Errorf("Test case %d: Expected error '%v' but received error '%v'", i+1, tc.expectedErr, actualErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestS3MetaToOSSOptions(t *testing.T) {
|
||||
var err error
|
||||
|
||||
headers := map[string]string{
|
||||
"x-amz-meta-invalid_meta": "value",
|
||||
}
|
||||
_, err = appendS3MetaToOSSOptions(minio.GlobalContext, nil, headers)
|
||||
if err != nil {
|
||||
if _, ok := err.(minio.UnsupportedMetadata); !ok {
|
||||
t.Fatalf("Test failed with unexpected error %s, expected UnsupportedMetadata", err)
|
||||
}
|
||||
}
|
||||
|
||||
headers = map[string]string{
|
||||
"accept-encoding": "gzip", // not this
|
||||
"content-encoding": "gzip",
|
||||
"X-Amz-Meta-Hdr": "value",
|
||||
"X-Amz-Meta-X-test-key": "value",
|
||||
"X-Amz-Meta-X--test--key": "value",
|
||||
"X-Amz-Meta-X-Amz-Key": "hu3ZSqtqwn+aL4V2VhAeov4i+bG3KyCtRMSXQFRHXOk=",
|
||||
"X-Amz-Meta-X-Amz-Matdesc": "{}",
|
||||
"X-Amz-Meta-X-Amz-Iv": "eWmyryl8kq+EVnnsE7jpOg==",
|
||||
}
|
||||
opts, err := appendS3MetaToOSSOptions(minio.GlobalContext, nil, headers)
|
||||
if err != nil {
|
||||
t.Fatalf("Test failed, with %s", err)
|
||||
}
|
||||
if len(opts) != len(headers) {
|
||||
t.Fatalf("Test failed, S3 metdata is not fully transformed. expeted: %d, actual: %d", len(headers)-1, len(opts))
|
||||
}
|
||||
}
|
||||
|
||||
func TestOSSHeaderToS3Meta(t *testing.T) {
|
||||
meta := map[string]string{
|
||||
"x-oss-meta-first_name": "myname",
|
||||
"X-OSS-Meta-x_test_key": "value",
|
||||
"X-Oss-Meta-x_test__key": "value",
|
||||
"X-Oss-Meta-x__test__key": "value",
|
||||
"X-Oss-Meta-x____test____key": "value",
|
||||
"X-Oss-Meta-x_amz_key": "hu3ZSqtqwn+aL4V2VhAeov4i+bG3KyCtRMSXQFRHXOk=",
|
||||
"X-Oss-Meta-x_amz_matdesc": "{}",
|
||||
"x-oss-meta-x_amz_iv": "eWmyryl8kq+EVnnsE7jpOg==",
|
||||
}
|
||||
header := make(http.Header)
|
||||
for k, v := range meta {
|
||||
header.Set(k, v)
|
||||
}
|
||||
|
||||
expectedMeta := map[string]string{
|
||||
"X-Amz-Meta-First-Name": "myname",
|
||||
"X-Amz-Meta-X-Test-Key": "value",
|
||||
"X-Amz-Meta-X-Test_key": "value",
|
||||
"X-Amz-Meta-X_test_key": "value",
|
||||
"X-Amz-Meta-X__test__key": "value",
|
||||
"X-Amz-Meta-X-Amz-Key": "hu3ZSqtqwn+aL4V2VhAeov4i+bG3KyCtRMSXQFRHXOk=",
|
||||
"X-Amz-Meta-X-Amz-Matdesc": "{}",
|
||||
"X-Amz-Meta-X-Amz-Iv": "eWmyryl8kq+EVnnsE7jpOg==",
|
||||
}
|
||||
actualMeta := ossHeaderToS3Meta(header)
|
||||
for k, v := range expectedMeta {
|
||||
if v2, ok := actualMeta[k]; !ok {
|
||||
t.Errorf("Test failed for key %s: missing key", k)
|
||||
} else if v != v2 {
|
||||
t.Errorf("Test failed for key %s, expected '%s', got '%s'", k, v, v2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOSSBuildListObjectPartsParams(t *testing.T) {
|
||||
expected := map[string]interface{}{
|
||||
"uploadId": "test",
|
||||
"part-number-marker": "123",
|
||||
"max-parts": "456",
|
||||
}
|
||||
actual := ossBuildListObjectPartsParams("test", 123, 456)
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Fatalf("Test failed, expected %v, got %v", expected, actual)
|
||||
}
|
||||
}
|
||||
@@ -315,7 +315,7 @@ func (l *s3EncObjects) GetObjectNInfo(ctx context.Context, bucket, object string
|
||||
return l.s3Objects.GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts)
|
||||
}
|
||||
objInfo.UserDefined = minio.CleanMinioInternalMetadataKeys(objInfo.UserDefined)
|
||||
fn, off, length, err := minio.NewGetObjectReader(rs, objInfo, o.CheckCopyPrecondFn)
|
||||
fn, off, length, err := minio.NewGetObjectReader(rs, objInfo, o)
|
||||
if err != nil {
|
||||
return nil, minio.ErrorRespToObjectError(err)
|
||||
}
|
||||
@@ -679,13 +679,13 @@ func getGWContentPath(object string) string {
|
||||
}
|
||||
|
||||
// Clean-up the stale incomplete encrypted multipart uploads. Should be run in a Go routine.
|
||||
func (l *s3EncObjects) cleanupStaleEncMultipartUploads(ctx context.Context, cleanupInterval, expiry time.Duration, doneCh <-chan struct{}) {
|
||||
func (l *s3EncObjects) cleanupStaleEncMultipartUploads(ctx context.Context, cleanupInterval, expiry time.Duration) {
|
||||
ticker := time.NewTicker(cleanupInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-doneCh:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
l.cleanupStaleEncMultipartUploadsOnGW(ctx, expiry)
|
||||
|
||||
@@ -242,7 +242,7 @@ func (g *S3) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error)
|
||||
|
||||
// Start stale enc multipart uploads cleanup routine.
|
||||
go encS.cleanupStaleEncMultipartUploads(minio.GlobalContext,
|
||||
minio.GlobalMultipartCleanupInterval, minio.GlobalMultipartExpiry, minio.GlobalServiceDoneCh)
|
||||
minio.GlobalMultipartCleanupInterval, minio.GlobalMultipartExpiry)
|
||||
|
||||
return &encS, nil
|
||||
}
|
||||
@@ -401,7 +401,7 @@ func (l *s3Objects) GetObjectNInfo(ctx context.Context, bucket, object string, r
|
||||
// Setup cleanup function to cause the above go-routine to
|
||||
// exit in case of partial read
|
||||
pipeCloser := func() { pr.Close() }
|
||||
return minio.NewGetObjectReaderFromReader(pr, objInfo, opts.CheckCopyPrecondFn, pipeCloser)
|
||||
return minio.NewGetObjectReaderFromReader(pr, objInfo, opts, pipeCloser)
|
||||
}
|
||||
|
||||
// GetObject reads an object from S3. Supports additional
|
||||
|
||||
+10
-9
@@ -233,11 +233,7 @@ func (iamOS *IAMObjectStore) loadIAMConfig(item interface{}, path string) error
|
||||
}
|
||||
|
||||
func (iamOS *IAMObjectStore) deleteIAMConfig(path string) error {
|
||||
err := deleteConfig(iamOS.ctx, iamOS.objAPI, path)
|
||||
if _, ok := err.(ObjectNotFound); ok {
|
||||
return errConfigNotFound
|
||||
}
|
||||
return err
|
||||
return deleteConfig(iamOS.ctx, iamOS.objAPI, path)
|
||||
}
|
||||
|
||||
func (iamOS *IAMObjectStore) loadPolicyDoc(policy string, m map[string]iampolicy.Policy) error {
|
||||
@@ -428,10 +424,12 @@ func (iamOS *IAMObjectStore) loadAll(ctx context.Context, sys *IAMSys) error {
|
||||
if err := iamOS.loadPolicyDocs(ctx, iamPolicyDocsMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// load STS temp users
|
||||
if err := iamOS.loadUsers(ctx, stsUser, iamUsersMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isMinIOUsersSys {
|
||||
if err := iamOS.loadUsers(ctx, regularUser, iamUsersMap); err != nil {
|
||||
return err
|
||||
@@ -442,15 +440,18 @@ func (iamOS *IAMObjectStore) loadAll(ctx context.Context, sys *IAMSys) error {
|
||||
if err := iamOS.loadGroups(ctx, iamGroupsMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := iamOS.loadMappedPolicies(ctx, regularUser, false, iamUserPolicyMap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// load polices mapped to users
|
||||
if err := iamOS.loadMappedPolicies(ctx, regularUser, false, iamUserPolicyMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// load STS policy mappings
|
||||
if err := iamOS.loadMappedPolicies(ctx, stsUser, false, iamUserPolicyMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// load policies mapped to groups
|
||||
if err := iamOS.loadMappedPolicies(ctx, regularUser, true, iamGroupPolicyMap); err != nil {
|
||||
return err
|
||||
|
||||
+7
-12
@@ -404,8 +404,7 @@ func (sys *IAMSys) DeletePolicy(policyName string) error {
|
||||
defer sys.store.unlock()
|
||||
|
||||
err := sys.store.deletePolicyDoc(policyName)
|
||||
switch err.(type) {
|
||||
case ObjectNotFound:
|
||||
if err == errNoSuchPolicy {
|
||||
// Ignore error if policy is already deleted.
|
||||
err = nil
|
||||
}
|
||||
@@ -417,7 +416,6 @@ func (sys *IAMSys) DeletePolicy(policyName string) error {
|
||||
var usersType []IAMUserType
|
||||
for u, mp := range sys.iamUserPolicyMap {
|
||||
if mp.Policy == policyName {
|
||||
usersToDel = append(usersToDel, u)
|
||||
cr, ok := sys.iamUsersMap[u]
|
||||
if !ok {
|
||||
// This case cannot happen
|
||||
@@ -429,6 +427,7 @@ func (sys *IAMSys) DeletePolicy(policyName string) error {
|
||||
} else {
|
||||
usersType = append(usersType, regularUser)
|
||||
}
|
||||
usersToDel = append(usersToDel, u)
|
||||
}
|
||||
}
|
||||
for i, u := range usersToDel {
|
||||
@@ -538,8 +537,7 @@ func (sys *IAMSys) DeleteUser(accessKey string) error {
|
||||
// It is ok to ignore deletion error on the mapped policy
|
||||
sys.store.deleteMappedPolicy(accessKey, regularUser, false)
|
||||
err := sys.store.deleteUserIdentity(accessKey, regularUser)
|
||||
switch err.(type) {
|
||||
case ObjectNotFound:
|
||||
if err == errNoSuchUser {
|
||||
// ignore if user is already deleted.
|
||||
err = nil
|
||||
}
|
||||
@@ -1020,14 +1018,11 @@ func (sys *IAMSys) RemoveUsersFromGroup(group string, members []string) error {
|
||||
// len(gi.Members) == 0 here.
|
||||
|
||||
// Remove the group from storage. First delete the
|
||||
// mapped policy.
|
||||
err := sys.store.deleteMappedPolicy(group, regularUser, true)
|
||||
// No-mapped-policy case is ignored.
|
||||
if err != nil && err != errNoSuchPolicy {
|
||||
// mapped policy. No-mapped-policy case is ignored.
|
||||
if err := sys.store.deleteMappedPolicy(group, regularUser, true); err != nil && err != errNoSuchPolicy {
|
||||
return err
|
||||
}
|
||||
err = sys.store.deleteGroupInfo(group)
|
||||
if err != nil {
|
||||
if err := sys.store.deleteGroupInfo(group); err != nil && err != errNoSuchGroup {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1195,7 +1190,7 @@ func (sys *IAMSys) policyDBSet(name, policy string, userType IAMUserType, isGrou
|
||||
|
||||
// Handle policy mapping removal
|
||||
if policy == "" {
|
||||
if err := sys.store.deleteMappedPolicy(name, userType, isGroup); err != nil {
|
||||
if err := sys.store.deleteMappedPolicy(name, userType, isGroup); err != nil && err != errNoSuchPolicy {
|
||||
return err
|
||||
}
|
||||
if !isGroup {
|
||||
|
||||
@@ -170,7 +170,7 @@ func newlockRESTClient(endpoint Endpoint) *lockRESTClient {
|
||||
}
|
||||
}
|
||||
|
||||
trFn := newCustomHTTPTransport(tlsConfig, rest.DefaultRESTTimeout, rest.DefaultRESTTimeout)
|
||||
trFn := newCustomHTTPTransport(tlsConfig, rest.DefaultRESTTimeout)
|
||||
restClient, err := rest.NewClient(serverURL, trFn, newAuthToken)
|
||||
if err != nil {
|
||||
logger.LogIf(GlobalContext, err)
|
||||
|
||||
+8
-3
@@ -56,18 +56,23 @@ type NotificationSys struct {
|
||||
}
|
||||
|
||||
// GetARNList - returns available ARNs.
|
||||
func (sys *NotificationSys) GetARNList() []string {
|
||||
func (sys *NotificationSys) GetARNList(onlyActive bool) []string {
|
||||
arns := []string{}
|
||||
if sys == nil {
|
||||
return arns
|
||||
}
|
||||
region := globalServerRegion
|
||||
for _, targetID := range sys.targetList.List() {
|
||||
for targetID, target := range sys.targetList.TargetMap() {
|
||||
// httpclient target is part of ListenBucketNotification
|
||||
// which doesn't need to be listed as part of the ARN list
|
||||
// This list is only meant for external targets, filter
|
||||
// this out pro-actively.
|
||||
if !strings.HasPrefix(targetID.ID, "httpclient+") {
|
||||
if onlyActive && !target.HasQueueStore() {
|
||||
if _, err := target.IsActive(); err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
arns = append(arns, targetID.ToARN(region).String())
|
||||
}
|
||||
}
|
||||
@@ -1171,7 +1176,7 @@ func NewNotificationSys(endpoints EndpointZones) *NotificationSys {
|
||||
targetList: event.NewTargetList(),
|
||||
bucketRulesMap: make(map[string]event.RulesMap),
|
||||
bucketRemoteTargetRulesMap: make(map[string]map[event.TargetID]event.RulesMap),
|
||||
peerClients: getRestClients(endpoints),
|
||||
peerClients: newPeerRestClients(endpoints),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-13
@@ -317,16 +317,14 @@ func getLocalProcOBD(ctx context.Context) madmin.ServerProcOBDInfo {
|
||||
sysProc.PageFaults = pageFaults
|
||||
|
||||
parent, err := proc.ParentWithContext(ctx)
|
||||
if err != nil {
|
||||
return errProcInfo(err)
|
||||
if err == nil {
|
||||
sysProc.Parent = parent.Pid
|
||||
}
|
||||
sysProc.Parent = parent.Pid
|
||||
|
||||
ppid, err := proc.PpidWithContext(ctx)
|
||||
if err != nil {
|
||||
return errProcInfo(err)
|
||||
if err == nil {
|
||||
sysProc.Ppid = ppid
|
||||
}
|
||||
sysProc.Ppid = ppid
|
||||
|
||||
rlimit, err := proc.RlimitWithContext(ctx)
|
||||
if err != nil {
|
||||
@@ -404,13 +402,8 @@ func getLocalOsInfoOBD(ctx context.Context) madmin.ServerOsOBDInfo {
|
||||
}
|
||||
}
|
||||
|
||||
users, err := host.UsersWithContext(ctx)
|
||||
if err != nil {
|
||||
return madmin.ServerOsOBDInfo{
|
||||
Addr: addr,
|
||||
Error: err.Error(),
|
||||
}
|
||||
}
|
||||
// ignore user err, as it cannot be obtained reliably inside containers
|
||||
users, _ := host.UsersWithContext(ctx)
|
||||
|
||||
return madmin.ServerOsOBDInfo{
|
||||
Addr: addr,
|
||||
|
||||
@@ -40,6 +40,7 @@ type GetObjectInfoFn func(ctx context.Context, bucket, object string, opts Objec
|
||||
type ObjectOptions struct {
|
||||
ServerSideEncryption encrypt.ServerSide
|
||||
UserDefined map[string]string
|
||||
PartNumber int
|
||||
CheckCopyPrecondFn CheckCopyPreconditionFn
|
||||
}
|
||||
|
||||
|
||||
+20
-15
@@ -485,15 +485,15 @@ type GetObjectReader struct {
|
||||
pReader io.Reader
|
||||
|
||||
cleanUpFns []func()
|
||||
precondFn func(ObjectInfo, string) bool
|
||||
opts ObjectOptions
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// NewGetObjectReaderFromReader sets up a GetObjectReader with a given
|
||||
// reader. This ignores any object properties.
|
||||
func NewGetObjectReaderFromReader(r io.Reader, oi ObjectInfo, pcfn CheckCopyPreconditionFn, cleanupFns ...func()) (*GetObjectReader, error) {
|
||||
if pcfn != nil {
|
||||
if ok := pcfn(oi, ""); ok {
|
||||
func NewGetObjectReaderFromReader(r io.Reader, oi ObjectInfo, opts ObjectOptions, cleanupFns ...func()) (*GetObjectReader, error) {
|
||||
if opts.CheckCopyPrecondFn != nil {
|
||||
if ok := opts.CheckCopyPrecondFn(oi, ""); ok {
|
||||
// Call the cleanup funcs
|
||||
for i := len(cleanupFns) - 1; i >= 0; i-- {
|
||||
cleanupFns[i]()
|
||||
@@ -505,7 +505,7 @@ func NewGetObjectReaderFromReader(r io.Reader, oi ObjectInfo, pcfn CheckCopyPrec
|
||||
ObjInfo: oi,
|
||||
pReader: r,
|
||||
cleanUpFns: cleanupFns,
|
||||
precondFn: pcfn,
|
||||
opts: opts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -519,7 +519,7 @@ type ObjReaderFn func(inputReader io.Reader, h http.Header, pcfn CheckCopyPrecon
|
||||
// are called on Close() in reverse order as passed here. NOTE: It is
|
||||
// assumed that clean up functions do not panic (otherwise, they may
|
||||
// not all run!).
|
||||
func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, pcfn CheckCopyPreconditionFn, cleanUpFns ...func()) (
|
||||
func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, opts ObjectOptions, cleanUpFns ...func()) (
|
||||
fn ObjReaderFn, off, length int64, err error) {
|
||||
|
||||
// Call the clean-up functions immediately in case of exit
|
||||
@@ -537,6 +537,7 @@ func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, pcfn CheckCopyPrecondi
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
var skipLen int64
|
||||
// Calculate range to read (different for
|
||||
// e.g. encrypted/compressed objects)
|
||||
@@ -581,8 +582,8 @@ func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, pcfn CheckCopyPrecondi
|
||||
encETag := oi.ETag
|
||||
oi.ETag = getDecryptedETag(h, oi, copySource) // Decrypt the ETag before top layer consumes this value.
|
||||
|
||||
if pcfn != nil {
|
||||
if ok := pcfn(oi, encETag); ok {
|
||||
if opts.CheckCopyPrecondFn != nil {
|
||||
if ok := opts.CheckCopyPrecondFn(oi, encETag); ok {
|
||||
// Call the cleanup funcs
|
||||
for i := len(cFns) - 1; i >= 0; i-- {
|
||||
cFns[i]()
|
||||
@@ -600,7 +601,7 @@ func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, pcfn CheckCopyPrecondi
|
||||
ObjInfo: oi,
|
||||
pReader: decReader,
|
||||
cleanUpFns: cFns,
|
||||
precondFn: pcfn,
|
||||
opts: opts,
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
@@ -634,8 +635,8 @@ func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, pcfn CheckCopyPrecondi
|
||||
}
|
||||
fn = func(inputReader io.Reader, _ http.Header, pcfn CheckCopyPreconditionFn, cFns ...func()) (r *GetObjectReader, err error) {
|
||||
cFns = append(cleanUpFns, cFns...)
|
||||
if pcfn != nil {
|
||||
if ok := pcfn(oi, ""); ok {
|
||||
if opts.CheckCopyPrecondFn != nil {
|
||||
if ok := opts.CheckCopyPrecondFn(oi, ""); ok {
|
||||
// Call the cleanup funcs
|
||||
for i := len(cFns) - 1; i >= 0; i-- {
|
||||
cFns[i]()
|
||||
@@ -648,6 +649,10 @@ func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, pcfn CheckCopyPrecondi
|
||||
// Apply the skipLen and limit on the decompressed stream.
|
||||
err = s2Reader.Skip(decOff)
|
||||
if err != nil {
|
||||
// Call the cleanup funcs
|
||||
for i := len(cFns) - 1; i >= 0; i-- {
|
||||
cFns[i]()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -668,7 +673,7 @@ func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, pcfn CheckCopyPrecondi
|
||||
ObjInfo: oi,
|
||||
pReader: decReader,
|
||||
cleanUpFns: cFns,
|
||||
precondFn: pcfn,
|
||||
opts: opts,
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
@@ -680,8 +685,8 @@ func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, pcfn CheckCopyPrecondi
|
||||
}
|
||||
fn = func(inputReader io.Reader, _ http.Header, pcfn CheckCopyPreconditionFn, cFns ...func()) (r *GetObjectReader, err error) {
|
||||
cFns = append(cleanUpFns, cFns...)
|
||||
if pcfn != nil {
|
||||
if ok := pcfn(oi, ""); ok {
|
||||
if opts.CheckCopyPrecondFn != nil {
|
||||
if ok := opts.CheckCopyPrecondFn(oi, ""); ok {
|
||||
// Call the cleanup funcs
|
||||
for i := len(cFns) - 1; i >= 0; i-- {
|
||||
cFns[i]()
|
||||
@@ -693,7 +698,7 @@ func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, pcfn CheckCopyPrecondi
|
||||
ObjInfo: oi,
|
||||
pReader: inputReader,
|
||||
cleanUpFns: cFns,
|
||||
precondFn: pcfn,
|
||||
opts: opts,
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func checkCopyObjectPreconditions(ctx context.Context, w http.ResponseWriter, r
|
||||
// If-Unmodified-Since
|
||||
// If-Match
|
||||
// If-None-Match
|
||||
func checkPreconditions(ctx context.Context, w http.ResponseWriter, r *http.Request, objInfo ObjectInfo) bool {
|
||||
func checkPreconditions(ctx context.Context, w http.ResponseWriter, r *http.Request, objInfo ObjectInfo, opts ObjectOptions) bool {
|
||||
// Return false for methods other than GET and HEAD.
|
||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||
return false
|
||||
@@ -170,6 +170,14 @@ func checkPreconditions(ctx context.Context, w http.ResponseWriter, r *http.Requ
|
||||
w.Header()[xhttp.ETag] = []string{"\"" + objInfo.ETag + "\""}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the part number is correct.
|
||||
if opts.PartNumber > 0 && opts.PartNumber != len(objInfo.Parts) {
|
||||
writeHeaders()
|
||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrPreconditionFailed), r.URL, guessIsBrowserReq(r))
|
||||
return true
|
||||
}
|
||||
|
||||
// If-Modified-Since : Return the object only if it has been modified since the specified time,
|
||||
// otherwise return a 304 (not modified).
|
||||
ifModifiedSinceHeader := r.Header.Get(xhttp.IfModifiedSince)
|
||||
|
||||
+52
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* MinIO Cloud Storage, (C) 2015-2018 MinIO, Inc.
|
||||
* MinIO Cloud Storage, (C) 2015-2020 MinIO, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"time"
|
||||
|
||||
@@ -73,7 +74,7 @@ const (
|
||||
// setHeadGetRespHeaders - set any requested parameters as response headers.
|
||||
func setHeadGetRespHeaders(w http.ResponseWriter, reqParams url.Values) {
|
||||
for k, v := range reqParams {
|
||||
if header, ok := supportedHeadGetReqParams[k]; ok {
|
||||
if header, ok := supportedHeadGetReqParams[strings.ToLower(k)]; ok {
|
||||
w.Header()[header] = v
|
||||
}
|
||||
}
|
||||
@@ -375,7 +376,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
|
||||
}
|
||||
|
||||
// Validate pre-conditions if any.
|
||||
if checkPreconditions(ctx, w, r, objInfo) {
|
||||
if checkPreconditions(ctx, w, r, objInfo, opts) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -565,7 +566,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
|
||||
}
|
||||
|
||||
// Validate pre-conditions if any.
|
||||
if checkPreconditions(ctx, w, r, objInfo) {
|
||||
if checkPreconditions(ctx, w, r, objInfo, opts) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -610,10 +611,29 @@ func getCpObjMetadataFromHeader(ctx context.Context, r *http.Request, userMeta m
|
||||
// remove SSE Headers from source info
|
||||
crypto.RemoveSSEHeaders(defaultMeta)
|
||||
|
||||
// Storage class is special, it can be replaced regardless of the
|
||||
// metadata directive, if set should be preserved and replaced
|
||||
// to the destination metadata.
|
||||
sc := r.Header.Get(xhttp.AmzStorageClass)
|
||||
if sc == "" {
|
||||
sc = r.URL.Query().Get(xhttp.AmzStorageClass)
|
||||
}
|
||||
|
||||
// if x-amz-metadata-directive says REPLACE then
|
||||
// we extract metadata from the input headers.
|
||||
if isDirectiveReplace(r.Header.Get(xhttp.AmzMetadataDirective)) {
|
||||
return extractMetadata(ctx, r)
|
||||
emetadata, err := extractMetadata(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sc != "" {
|
||||
emetadata[xhttp.AmzStorageClass] = sc
|
||||
}
|
||||
return emetadata, nil
|
||||
}
|
||||
|
||||
if sc != "" {
|
||||
defaultMeta[xhttp.AmzStorageClass] = sc
|
||||
}
|
||||
|
||||
// if x-amz-metadata-directive says COPY then we
|
||||
@@ -644,6 +664,10 @@ func getCpObjTagsFromHeader(ctx context.Context, r *http.Request, tags string) (
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
// getRemoteInstanceTransport contains a singleton roundtripper.
|
||||
var getRemoteInstanceTransport http.RoundTripper
|
||||
var getRemoteInstanceTransportOnce sync.Once
|
||||
|
||||
// Returns a minio-go Client configured to access remote host described by destDNSRecord
|
||||
// Applicable only in a federated deployment
|
||||
var getRemoteInstanceClient = func(r *http.Request, host string) (*miniogo.Core, error) {
|
||||
@@ -654,7 +678,10 @@ var getRemoteInstanceClient = func(r *http.Request, host string) (*miniogo.Core,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
core.SetCustomTransport(NewGatewayHTTPTransport())
|
||||
getRemoteInstanceTransportOnce.Do(func() {
|
||||
getRemoteInstanceTransport = NewGatewayHTTPTransport()
|
||||
})
|
||||
core.SetCustomTransport(getRemoteInstanceTransport)
|
||||
return core, nil
|
||||
}
|
||||
|
||||
@@ -778,6 +805,13 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
||||
return
|
||||
}
|
||||
|
||||
// Validate storage class metadata if present
|
||||
dstSc := r.Header.Get(xhttp.AmzStorageClass)
|
||||
if dstSc != "" && !storageclass.IsValid(dstSc) {
|
||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidStorageClass), r.URL, guessIsBrowserReq(r))
|
||||
return
|
||||
}
|
||||
|
||||
// Check if bucket encryption is enabled
|
||||
_, encEnabled := globalBucketSSEConfigSys.Get(dstBucket)
|
||||
// This request header needs to be set prior to setting ObjectOptions
|
||||
@@ -842,6 +876,15 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
||||
srcInfo.metadataOnly = true
|
||||
}
|
||||
|
||||
var chStorageClass bool
|
||||
if dstSc != "" {
|
||||
sc, ok := srcInfo.UserDefined[xhttp.AmzStorageClass]
|
||||
if (ok && dstSc != sc) || (srcInfo.StorageClass != dstSc) {
|
||||
chStorageClass = true
|
||||
srcInfo.metadataOnly = false
|
||||
}
|
||||
}
|
||||
|
||||
var reader io.Reader
|
||||
var length = srcInfo.Size
|
||||
|
||||
@@ -923,9 +966,10 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
||||
// If src == dst and either
|
||||
// - the object is encrypted using SSE-C and two different SSE-C keys are present
|
||||
// - the object is encrypted using SSE-S3 and the SSE-S3 header is present
|
||||
// than execute a key rotation.
|
||||
// - the object storage class is not changing
|
||||
// then execute a key rotation.
|
||||
var keyRotation bool
|
||||
if cpSrcDstSame && (sseCopyC && sseC) {
|
||||
if cpSrcDstSame && (sseCopyC && sseC) && !chStorageClass {
|
||||
oldKey, err = ParseSSECopyCustomerRequest(r.Header, srcInfo.UserDefined)
|
||||
if err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||
|
||||
@@ -36,6 +36,11 @@ func (target *PeerRESTClientTarget) IsActive() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// HasQueueStore - No-Op. Added for interface compatibility
|
||||
func (target PeerRESTClientTarget) HasQueueStore() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Save - Sends event directly without persisting.
|
||||
func (target *PeerRESTClientTarget) Save(eventData event.Event) error {
|
||||
return target.send(eventData)
|
||||
|
||||
+13
-9
@@ -374,7 +374,11 @@ func (client *peerRESTClient) DispatchNetOBDInfo(ctx context.Context) (info madm
|
||||
return
|
||||
}
|
||||
defer http.DrainBody(respBody)
|
||||
err = gob.NewDecoder(respBody).Decode(&info)
|
||||
waitReader, err := waitForHTTPResponse(respBody)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = gob.NewDecoder(waitReader).Decode(&info)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -834,7 +838,7 @@ func (client *peerRESTClient) BackgroundHealStatus() (madmin.BgHealState, error)
|
||||
return state, err
|
||||
}
|
||||
|
||||
func (client *peerRESTClient) doTrace(traceCh chan interface{}, doneCh chan struct{}, trcAll, trcErr bool) {
|
||||
func (client *peerRESTClient) doTrace(traceCh chan interface{}, doneCh <-chan struct{}, trcAll, trcErr bool) {
|
||||
values := make(url.Values)
|
||||
values.Set(peerRESTTraceAll, strconv.FormatBool(trcAll))
|
||||
values.Set(peerRESTTraceErr, strconv.FormatBool(trcErr))
|
||||
@@ -876,7 +880,7 @@ func (client *peerRESTClient) doTrace(traceCh chan interface{}, doneCh chan stru
|
||||
}
|
||||
}
|
||||
|
||||
func (client *peerRESTClient) doListen(listenCh chan interface{}, doneCh chan struct{}, v url.Values) {
|
||||
func (client *peerRESTClient) doListen(listenCh chan interface{}, doneCh <-chan struct{}, v url.Values) {
|
||||
// To cancel the REST request in case doneCh gets closed.
|
||||
ctx, cancel := context.WithCancel(GlobalContext)
|
||||
|
||||
@@ -915,7 +919,7 @@ func (client *peerRESTClient) doListen(listenCh chan interface{}, doneCh chan st
|
||||
}
|
||||
|
||||
// Listen - listen on peers.
|
||||
func (client *peerRESTClient) Listen(listenCh chan interface{}, doneCh chan struct{}, v url.Values) {
|
||||
func (client *peerRESTClient) Listen(listenCh chan interface{}, doneCh <-chan struct{}, v url.Values) {
|
||||
go func() {
|
||||
for {
|
||||
client.doListen(listenCh, doneCh, v)
|
||||
@@ -931,7 +935,7 @@ func (client *peerRESTClient) Listen(listenCh chan interface{}, doneCh chan stru
|
||||
}
|
||||
|
||||
// Trace - send http trace request to peer nodes
|
||||
func (client *peerRESTClient) Trace(traceCh chan interface{}, doneCh chan struct{}, trcAll, trcErr bool) {
|
||||
func (client *peerRESTClient) Trace(traceCh chan interface{}, doneCh <-chan struct{}, trcAll, trcErr bool) {
|
||||
go func() {
|
||||
for {
|
||||
client.doTrace(traceCh, doneCh, trcAll, trcErr)
|
||||
@@ -947,7 +951,7 @@ func (client *peerRESTClient) Trace(traceCh chan interface{}, doneCh chan struct
|
||||
}
|
||||
|
||||
// ConsoleLog - sends request to peer nodes to get console logs
|
||||
func (client *peerRESTClient) ConsoleLog(logCh chan interface{}, doneCh chan struct{}) {
|
||||
func (client *peerRESTClient) ConsoleLog(logCh chan interface{}, doneCh <-chan struct{}) {
|
||||
go func() {
|
||||
for {
|
||||
// get cancellation context to properly unsubscribe peers
|
||||
@@ -1002,7 +1006,8 @@ func getRemoteHosts(endpointZones EndpointZones) []*xnet.Host {
|
||||
return remoteHosts
|
||||
}
|
||||
|
||||
func getRestClients(endpoints EndpointZones) []*peerRESTClient {
|
||||
// newPeerRestClients creates new peer clients.
|
||||
func newPeerRestClients(endpoints EndpointZones) []*peerRESTClient {
|
||||
peerHosts := getRemoteHosts(endpoints)
|
||||
restClients := make([]*peerRESTClient, len(peerHosts))
|
||||
for i, host := range peerHosts {
|
||||
@@ -1019,7 +1024,6 @@ func getRestClients(endpoints EndpointZones) []*peerRESTClient {
|
||||
|
||||
// Returns a peer rest client.
|
||||
func newPeerRESTClient(peer *xnet.Host) (*peerRESTClient, error) {
|
||||
|
||||
scheme := "http"
|
||||
if globalIsSSL {
|
||||
scheme = "https"
|
||||
@@ -1039,7 +1043,7 @@ func newPeerRESTClient(peer *xnet.Host) (*peerRESTClient, error) {
|
||||
}
|
||||
}
|
||||
|
||||
trFn := newCustomHTTPTransport(tlsConfig, rest.DefaultRESTTimeout, rest.DefaultRESTTimeout)
|
||||
trFn := newCustomHTTPTransport(tlsConfig, rest.DefaultRESTTimeout)
|
||||
restClient, err := rest.NewClient(serverURL, trFn, newAuthToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -59,7 +59,7 @@ func getServerInfo() (*ServerInfoData, error) {
|
||||
Version: Version,
|
||||
CommitID: CommitID,
|
||||
DeploymentID: globalDeploymentID,
|
||||
SQSARN: globalNotificationSys.GetARNList(),
|
||||
SQSARN: globalNotificationSys.GetARNList(false),
|
||||
Region: globalServerRegion,
|
||||
},
|
||||
}, nil
|
||||
@@ -403,9 +403,12 @@ func (s *peerRESTServer) DispatchNetOBDInfoHandler(w http.ResponseWriter, r *htt
|
||||
return
|
||||
}
|
||||
|
||||
done := keepHTTPResponseAlive(w)
|
||||
|
||||
ctx := newContext(r, w, "DispatchNetOBDInfo")
|
||||
info := globalNotificationSys.NetOBDInfo(ctx)
|
||||
|
||||
done()
|
||||
logger.LogIf(ctx, gob.NewEncoder(w).Encode(info))
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
@@ -1161,7 +1164,7 @@ func registerPeerRESTHandlers(router *mux.Router) {
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodDeletePolicy).HandlerFunc(httpTraceAll(server.DeletePolicyHandler)).Queries(restQueries(peerRESTPolicy)...)
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodLoadPolicy).HandlerFunc(httpTraceAll(server.LoadPolicyHandler)).Queries(restQueries(peerRESTPolicy)...)
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodLoadPolicyMapping).HandlerFunc(httpTraceAll(server.LoadPolicyMappingHandler)).Queries(restQueries(peerRESTUserOrGroup)...)
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodDeleteUser).HandlerFunc(httpTraceAll(server.LoadUserHandler)).Queries(restQueries(peerRESTUser)...)
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodDeleteUser).HandlerFunc(httpTraceAll(server.DeleteUserHandler)).Queries(restQueries(peerRESTUser)...)
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodLoadUser).HandlerFunc(httpTraceAll(server.LoadUserHandler)).Queries(restQueries(peerRESTUser, peerRESTUserTemp)...)
|
||||
subrouter.Methods(http.MethodPost).Path(peerRESTVersionPrefix + peerRESTMethodLoadGroup).HandlerFunc(httpTraceAll(server.LoadGroupHandler)).Queries(restQueries(peerRESTGroup)...)
|
||||
|
||||
|
||||
@@ -201,12 +201,12 @@ func IsServerResolvable(endpoint Endpoint) error {
|
||||
}
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: newCustomHTTPTransport(tlsConfig, rest.DefaultRESTTimeout, rest.DefaultRESTTimeout)(),
|
||||
Transport: newCustomHTTPTransport(tlsConfig, rest.DefaultRESTTimeout)(),
|
||||
}
|
||||
defer httpClient.CloseIdleConnections()
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
httpClient.CloseIdleConnections()
|
||||
return err
|
||||
}
|
||||
defer xhttp.DrainBody(resp.Body)
|
||||
|
||||
+8
-5
@@ -75,12 +75,15 @@ EXAMPLES:
|
||||
1. Start minio server on "/home/shared" directory.
|
||||
{{.Prompt}} {{.HelpName}} /home/shared
|
||||
|
||||
2. Start distributed minio server on an 32 node setup with 32 drives each, run following command on all the nodes
|
||||
2. Start single node server with 64 local drives "/mnt/data1" to "/mnt/data64".
|
||||
{{.Prompt}} {{.HelpName}} /mnt/data{1...64}
|
||||
|
||||
3. Start distributed minio server on an 32 node setup with 32 drives each, run following command on all the nodes
|
||||
{{.Prompt}} {{.EnvVarSetCommand}} MINIO_ACCESS_KEY{{.AssignmentOperator}}minio
|
||||
{{.Prompt}} {{.EnvVarSetCommand}} MINIO_SECRET_KEY{{.AssignmentOperator}}miniostorage
|
||||
{{.Prompt}} {{.HelpName}} http://node{1...32}.example.com/mnt/export{1...32}
|
||||
|
||||
3. Start distributed minio server in an expanded setup, run the following command on all the nodes
|
||||
4. Start distributed minio server in an expanded setup, run the following command on all the nodes
|
||||
{{.Prompt}} {{.EnvVarSetCommand}} MINIO_ACCESS_KEY{{.AssignmentOperator}}minio
|
||||
{{.Prompt}} {{.EnvVarSetCommand}} MINIO_SECRET_KEY{{.AssignmentOperator}}miniostorage
|
||||
{{.Prompt}} {{.HelpName}} http://node{1...16}.example.com/mnt/export{1...32} \
|
||||
@@ -412,7 +415,7 @@ func serverMain(ctx *cli.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
newObject, err := newObjectLayer(globalEndpoints)
|
||||
newObject, err := newObjectLayer(GlobalContext, globalEndpoints)
|
||||
logger.SetDeploymentID(globalDeploymentID)
|
||||
if err != nil {
|
||||
// Stop watching for any certificate changes.
|
||||
@@ -479,7 +482,7 @@ func serverMain(ctx *cli.Context) {
|
||||
}
|
||||
|
||||
// Initialize object layer with the supplied disks, objectLayer is nil upon any error.
|
||||
func newObjectLayer(endpointZones EndpointZones) (newObject ObjectLayer, err error) {
|
||||
func newObjectLayer(ctx context.Context, endpointZones EndpointZones) (newObject ObjectLayer, err error) {
|
||||
// For FS only, directly use the disk.
|
||||
|
||||
if endpointZones.NEndpoints() == 1 {
|
||||
@@ -487,5 +490,5 @@ func newObjectLayer(endpointZones EndpointZones) (newObject ObjectLayer, err err
|
||||
return NewFSObjectLayer(endpointZones[0].Endpoints[0].Path)
|
||||
}
|
||||
|
||||
return newXLZones(endpointZones)
|
||||
return newXLZones(ctx, endpointZones)
|
||||
}
|
||||
|
||||
@@ -17,12 +17,15 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests initializing new object layer.
|
||||
func TestNewObjectLayer(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Tests for FS object layer.
|
||||
nDisks := 1
|
||||
disks, err := getRandomDisks(nDisks)
|
||||
@@ -31,7 +34,7 @@ func TestNewObjectLayer(t *testing.T) {
|
||||
}
|
||||
defer removeRoots(disks)
|
||||
|
||||
obj, err := newObjectLayer(mustGetZoneEndpoints(disks...))
|
||||
obj, err := newObjectLayer(ctx, mustGetZoneEndpoints(disks...))
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected object layer initialization error", err)
|
||||
}
|
||||
@@ -50,7 +53,7 @@ func TestNewObjectLayer(t *testing.T) {
|
||||
}
|
||||
defer removeRoots(disks)
|
||||
|
||||
obj, err = newObjectLayer(mustGetZoneEndpoints(disks...))
|
||||
obj, err = newObjectLayer(ctx, mustGetZoneEndpoints(disks...))
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected object layer initialization error", err)
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ func printEventNotifiers() {
|
||||
return
|
||||
}
|
||||
|
||||
arns := globalNotificationSys.GetARNList()
|
||||
arns := globalNotificationSys.GetARNList(true)
|
||||
if len(arns) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -572,7 +572,7 @@ func newStorageRESTClient(endpoint Endpoint) *storageRESTClient {
|
||||
}
|
||||
}
|
||||
|
||||
trFn := newCustomHTTPTransport(tlsConfig, rest.DefaultRESTTimeout, rest.DefaultRESTTimeout)
|
||||
trFn := newCustomHTTPTransport(tlsConfig, rest.DefaultRESTTimeout)
|
||||
restClient, err := rest.NewClient(serverURL, trFn, newAuthToken)
|
||||
if err != nil {
|
||||
logger.LogIf(GlobalContext, err)
|
||||
|
||||
+43
-25
@@ -160,7 +160,7 @@ func prepareFS() (ObjectLayer, string, error) {
|
||||
return obj, fsDirs[0], nil
|
||||
}
|
||||
|
||||
func prepareXLSets32() (ObjectLayer, []string, error) {
|
||||
func prepareXLSets32(ctx context.Context) (ObjectLayer, []string, error) {
|
||||
fsDirs1, err := getRandomDisks(16)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -182,7 +182,7 @@ func prepareXLSets32() (ObjectLayer, []string, error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
objAPI, err := newXLSets(endpoints, storageDisks, format, 2, 16)
|
||||
objAPI, err := newXLSets(ctx, endpoints, storageDisks, format, 2, 16)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -190,12 +190,12 @@ func prepareXLSets32() (ObjectLayer, []string, error) {
|
||||
return objAPI, fsDirs, nil
|
||||
}
|
||||
|
||||
func prepareXL(nDisks int) (ObjectLayer, []string, error) {
|
||||
func prepareXL(ctx context.Context, nDisks int) (ObjectLayer, []string, error) {
|
||||
fsDirs, err := getRandomDisks(nDisks)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
obj, _, err := initObjectLayer(mustGetZoneEndpoints(fsDirs...))
|
||||
obj, _, err := initObjectLayer(ctx, mustGetZoneEndpoints(fsDirs...))
|
||||
if err != nil {
|
||||
removeRoots(fsDirs)
|
||||
return nil, nil, err
|
||||
@@ -203,8 +203,8 @@ func prepareXL(nDisks int) (ObjectLayer, []string, error) {
|
||||
return obj, fsDirs, nil
|
||||
}
|
||||
|
||||
func prepareXL16() (ObjectLayer, []string, error) {
|
||||
return prepareXL(16)
|
||||
func prepareXL16(ctx context.Context) (ObjectLayer, []string, error) {
|
||||
return prepareXL(ctx, 16)
|
||||
}
|
||||
|
||||
// Initialize FS objects.
|
||||
@@ -292,16 +292,18 @@ type TestServer struct {
|
||||
SecretKey string
|
||||
Server *httptest.Server
|
||||
Obj ObjectLayer
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
// UnstartedTestServer - Configures a temp FS/XL backend,
|
||||
// initializes the endpoints and configures the test server.
|
||||
// The server should be started using the Start() method.
|
||||
func UnstartedTestServer(t TestErrHandler, instanceType string) TestServer {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
// create an instance of TestServer.
|
||||
testServer := TestServer{}
|
||||
testServer := TestServer{cancel: cancel}
|
||||
// return FS/XL object layer and temp backend.
|
||||
objLayer, disks, err := prepareTestBackend(instanceType)
|
||||
objLayer, disks, err := prepareTestBackend(ctx, instanceType)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -341,9 +343,9 @@ func UnstartedTestServer(t TestErrHandler, instanceType string) TestServer {
|
||||
globalConfigSys = NewConfigSys()
|
||||
|
||||
globalIAMSys = NewIAMSys()
|
||||
globalIAMSys.Init(GlobalContext, objLayer)
|
||||
globalIAMSys.Init(ctx, objLayer)
|
||||
|
||||
buckets, err := objLayer.ListBuckets(context.Background())
|
||||
buckets, err := objLayer.ListBuckets(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to list buckets on backend %s", err)
|
||||
}
|
||||
@@ -503,13 +505,14 @@ func newTestConfig(bucketLocation string, obj ObjectLayer) (err error) {
|
||||
|
||||
// Deleting the temporary backend and stopping the server.
|
||||
func (testServer TestServer) Stop() {
|
||||
testServer.cancel()
|
||||
testServer.Server.Close()
|
||||
os.RemoveAll(testServer.Root)
|
||||
for _, ep := range testServer.Disks {
|
||||
for _, disk := range ep.Endpoints {
|
||||
os.RemoveAll(disk.Path)
|
||||
}
|
||||
}
|
||||
testServer.Server.Close()
|
||||
}
|
||||
|
||||
// Truncate request to simulate unexpected EOF for a request signed using streaming signature v4.
|
||||
@@ -1568,14 +1571,14 @@ func getRandomDisks(N int) ([]string, error) {
|
||||
}
|
||||
|
||||
// Initialize object layer with the supplied disks, objectLayer is nil upon any error.
|
||||
func newTestObjectLayer(endpointZones EndpointZones) (newObject ObjectLayer, err error) {
|
||||
func newTestObjectLayer(ctx context.Context, endpointZones EndpointZones) (newObject ObjectLayer, err error) {
|
||||
// For FS only, directly use the disk.
|
||||
if endpointZones.NEndpoints() == 1 {
|
||||
// Initialize new FS object layer.
|
||||
return NewFSObjectLayer(endpointZones[0].Endpoints[0].Path)
|
||||
}
|
||||
|
||||
z, err := newXLZones(endpointZones)
|
||||
z, err := newXLZones(ctx, endpointZones)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1595,8 +1598,8 @@ func newTestObjectLayer(endpointZones EndpointZones) (newObject ObjectLayer, err
|
||||
}
|
||||
|
||||
// initObjectLayer - Instantiates object layer and returns it.
|
||||
func initObjectLayer(endpointZones EndpointZones) (ObjectLayer, []StorageAPI, error) {
|
||||
objLayer, err := newTestObjectLayer(endpointZones)
|
||||
func initObjectLayer(ctx context.Context, endpointZones EndpointZones) (ObjectLayer, []StorageAPI, error) {
|
||||
objLayer, err := newTestObjectLayer(ctx, endpointZones)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -1655,14 +1658,14 @@ func initAPIHandlerTest(obj ObjectLayer, endpoints []string) (string, http.Handl
|
||||
// prepare test backend.
|
||||
// create FS/XL/XLSet backend.
|
||||
// return object layer, backend disks.
|
||||
func prepareTestBackend(instanceType string) (ObjectLayer, []string, error) {
|
||||
func prepareTestBackend(ctx context.Context, instanceType string) (ObjectLayer, []string, error) {
|
||||
switch instanceType {
|
||||
// Total number of disks for XL sets backend is set to 32.
|
||||
case XLSetsTestStr:
|
||||
return prepareXLSets32()
|
||||
return prepareXLSets32(ctx)
|
||||
// Total number of disks for XL backend is set to 16.
|
||||
case XLTestStr:
|
||||
return prepareXL16()
|
||||
return prepareXL16(ctx)
|
||||
default:
|
||||
// return FS backend by default.
|
||||
obj, disk, err := prepareFS()
|
||||
@@ -1866,6 +1869,9 @@ func ExecObjectLayerAPINilTest(t TestErrHandler, bucketName, objectName, instanc
|
||||
// ExecObjectLayerAPITest - executes object layer API tests.
|
||||
// Creates single node and XL ObjectLayer instance, registers the specified API end points and runs test for both the layers.
|
||||
func ExecObjectLayerAPITest(t *testing.T, objAPITest objAPITestType, endpoints []string) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// reset globals.
|
||||
// this is to make sure that the tests are not affected by modified value.
|
||||
resetTestGlobals()
|
||||
@@ -1901,7 +1907,7 @@ func ExecObjectLayerAPITest(t *testing.T, objAPITest objAPITestType, endpoints [
|
||||
// Executing the object layer tests for single node setup.
|
||||
objAPITest(objLayer, FSTestStr, bucketFS, fsAPIRouter, credentials, t)
|
||||
|
||||
objLayer, xlDisks, err := prepareXL16()
|
||||
objLayer, xlDisks, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Initialization of object layer failed for XL setup: %s", err)
|
||||
}
|
||||
@@ -1931,6 +1937,9 @@ type objTestDiskNotFoundType func(obj ObjectLayer, instanceType string, dirs []s
|
||||
// ExecObjectLayerTest - executes object layer tests.
|
||||
// Creates single node and XL ObjectLayer instance and runs test for both the layers.
|
||||
func ExecObjectLayerTest(t TestErrHandler, objTest objTestType) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
objLayer, fsDir, err := prepareFS()
|
||||
if err != nil {
|
||||
t.Fatalf("Initialization of object layer failed for single node setup: %s", err)
|
||||
@@ -1943,7 +1952,7 @@ func ExecObjectLayerTest(t TestErrHandler, objTest objTestType) {
|
||||
}
|
||||
|
||||
globalIAMSys = NewIAMSys()
|
||||
globalIAMSys.Init(GlobalContext, objLayer)
|
||||
globalIAMSys.Init(ctx, objLayer)
|
||||
|
||||
buckets, err := objLayer.ListBuckets(context.Background())
|
||||
if err != nil {
|
||||
@@ -1959,19 +1968,22 @@ func ExecObjectLayerTest(t TestErrHandler, objTest objTestType) {
|
||||
// Executing the object layer tests for single node setup.
|
||||
objTest(objLayer, FSTestStr, t)
|
||||
|
||||
objLayer, fsDirs, err := prepareXLSets32()
|
||||
objLayer, fsDirs, err := prepareXLSets32(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Initialization of object layer failed for XL setup: %s", err)
|
||||
}
|
||||
defer removeRoots(append(fsDirs, fsDir))
|
||||
// Executing the object layer tests for XL.
|
||||
objTest(objLayer, XLTestStr, t)
|
||||
defer removeRoots(append(fsDirs, fsDir))
|
||||
}
|
||||
|
||||
// ExecObjectLayerTestWithDirs - executes object layer tests.
|
||||
// Creates single node and XL ObjectLayer instance and runs test for both the layers.
|
||||
func ExecObjectLayerTestWithDirs(t TestErrHandler, objTest objTestTypeWithDirs) {
|
||||
objLayer, fsDirs, err := prepareXL16()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
objLayer, fsDirs, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Initialization of object layer failed for XL setup: %s", err)
|
||||
}
|
||||
@@ -1990,7 +2002,10 @@ func ExecObjectLayerTestWithDirs(t TestErrHandler, objTest objTestTypeWithDirs)
|
||||
// ExecObjectLayerDiskAlteredTest - executes object layer tests while altering
|
||||
// disks in between tests. Creates XL ObjectLayer instance and runs test for XL layer.
|
||||
func ExecObjectLayerDiskAlteredTest(t *testing.T, objTest objTestDiskNotFoundType) {
|
||||
objLayer, fsDirs, err := prepareXL16()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
objLayer, fsDirs, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Initialization of object layer failed for XL setup: %s", err)
|
||||
}
|
||||
@@ -2010,12 +2025,15 @@ type objTestStaleFilesType func(obj ObjectLayer, instanceType string, dirs []str
|
||||
// ExecObjectLayerStaleFilesTest - executes object layer tests those leaves stale
|
||||
// files/directories under .minio/tmp. Creates XL ObjectLayer instance and runs test for XL layer.
|
||||
func ExecObjectLayerStaleFilesTest(t *testing.T, objTest objTestStaleFilesType) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
nDisks := 16
|
||||
erasureDisks, err := getRandomDisks(nDisks)
|
||||
if err != nil {
|
||||
t.Fatalf("Initialization of disks for XL setup: %s", err)
|
||||
}
|
||||
objLayer, _, err := initObjectLayer(mustGetZoneEndpoints(erasureDisks...))
|
||||
objLayer, _, err := initObjectLayer(ctx, mustGetZoneEndpoints(erasureDisks...))
|
||||
if err != nil {
|
||||
t.Fatalf("Initialization of object layer failed for XL setup: %s", err)
|
||||
}
|
||||
|
||||
+13
-8
@@ -172,8 +172,7 @@ const (
|
||||
globalMaxPartID = 10000
|
||||
|
||||
// Default values used while communicating for internode communication.
|
||||
defaultDialTimeout = 5 * time.Second
|
||||
defaultDialKeepAlive = 15 * time.Second
|
||||
defaultDialTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
// isMaxObjectSize - verify if max object size
|
||||
@@ -464,14 +463,15 @@ func newCustomDialContext(dialTimeout, dialKeepAlive time.Duration) dialContext
|
||||
}
|
||||
}
|
||||
|
||||
func newCustomHTTPTransport(tlsConfig *tls.Config, dialTimeout, dialKeepAlive time.Duration) func() *http.Transport {
|
||||
func newCustomHTTPTransport(tlsConfig *tls.Config, dialTimeout time.Duration) func() *http.Transport {
|
||||
// For more details about various values used here refer
|
||||
// https://golang.org/pkg/net/http/#Transport documentation
|
||||
tr := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: newCustomDialContext(dialTimeout, dialKeepAlive),
|
||||
MaxIdleConnsPerHost: 256,
|
||||
IdleConnTimeout: time.Minute,
|
||||
DialContext: newCustomDialContext(dialTimeout, 15*time.Second),
|
||||
MaxIdleConnsPerHost: 16,
|
||||
MaxIdleConns: 16,
|
||||
IdleConnTimeout: 1 * time.Minute,
|
||||
ResponseHeaderTimeout: 3 * time.Minute, // Set conservative timeouts for MinIO internode.
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 10 * time.Second,
|
||||
@@ -493,9 +493,14 @@ func newCustomHTTPTransport(tlsConfig *tls.Config, dialTimeout, dialKeepAlive ti
|
||||
func NewGatewayHTTPTransport() *http.Transport {
|
||||
tr := newCustomHTTPTransport(&tls.Config{
|
||||
RootCAs: globalRootCAs,
|
||||
}, defaultDialTimeout, defaultDialKeepAlive)()
|
||||
}, defaultDialTimeout)()
|
||||
// Set aggressive timeouts for gateway
|
||||
tr.ResponseHeaderTimeout = 30 * time.Second
|
||||
tr.ResponseHeaderTimeout = 1 * time.Minute
|
||||
|
||||
// Allow more requests to be in flight.
|
||||
tr.MaxConnsPerHost = 256
|
||||
tr.MaxIdleConnsPerHost = 16
|
||||
tr.MaxIdleConns = 256
|
||||
return tr
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -2109,10 +2109,10 @@ func (web *webAPIHandlers) LoginSTS(r *http.Request, args *LoginSTSArgs, reply *
|
||||
clnt := &http.Client{
|
||||
Transport: NewGatewayHTTPTransport(),
|
||||
}
|
||||
defer clnt.CloseIdleConnections()
|
||||
|
||||
resp, err := clnt.Do(req)
|
||||
if err != nil {
|
||||
clnt.CloseIdleConnections()
|
||||
return toJSONError(ctx, err)
|
||||
}
|
||||
defer xhttp.DrainBody(resp.Body)
|
||||
|
||||
@@ -1475,8 +1475,11 @@ func testWebSetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestE
|
||||
|
||||
// TestWebCheckAuthorization - Test Authorization for all web handlers
|
||||
func TestWebCheckAuthorization(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Prepare XL backend
|
||||
obj, fsDirs, err := prepareXL16()
|
||||
obj, fsDirs, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Initialization of object layer failed for XL setup: %s", err)
|
||||
}
|
||||
@@ -1564,8 +1567,11 @@ func TestWebCheckAuthorization(t *testing.T) {
|
||||
|
||||
// TestWebObjectLayerFaultyDisks - Test Web RPC responses with faulty disks
|
||||
func TestWebObjectLayerFaultyDisks(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Prepare XL backend
|
||||
obj, fsDirs, err := prepareXL16()
|
||||
obj, fsDirs, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Initialization of object layer failed for XL setup: %s", err)
|
||||
}
|
||||
|
||||
+4
-4
@@ -293,7 +293,7 @@ func (s *xlSets) GetDisks(setIndex int) func() []StorageAPI {
|
||||
const defaultMonitorConnectEndpointInterval = time.Second * 10 // Set to 10 secs.
|
||||
|
||||
// Initialize new set of erasure coded sets.
|
||||
func newXLSets(endpoints Endpoints, storageDisks []StorageAPI, format *formatXLV3, setCount int, drivesPerSet int) (*xlSets, error) {
|
||||
func newXLSets(ctx context.Context, endpoints Endpoints, storageDisks []StorageAPI, format *formatXLV3, setCount int, drivesPerSet int) (*xlSets, error) {
|
||||
endpointStrings := make([]string, len(endpoints))
|
||||
for i, endpoint := range endpoints {
|
||||
if endpoint.IsLocal {
|
||||
@@ -363,12 +363,12 @@ func newXLSets(endpoints Endpoints, storageDisks []StorageAPI, format *formatXLV
|
||||
mrfUploadCh: make(chan partialUpload, 10000),
|
||||
}
|
||||
|
||||
go s.sets[i].cleanupStaleMultipartUploads(GlobalContext,
|
||||
GlobalMultipartCleanupInterval, GlobalMultipartExpiry, GlobalServiceDoneCh)
|
||||
go s.sets[i].cleanupStaleMultipartUploads(ctx,
|
||||
GlobalMultipartCleanupInterval, GlobalMultipartExpiry, ctx.Done())
|
||||
}
|
||||
|
||||
// Start the disk monitoring and connect routine.
|
||||
go s.monitorAndConnectEndpoints(GlobalContext, defaultMonitorConnectEndpointInterval)
|
||||
go s.monitorAndConnectEndpoints(ctx, defaultMonitorConnectEndpointInterval)
|
||||
go s.maintainMRFList()
|
||||
go s.healMRFRoutine()
|
||||
|
||||
|
||||
+9
-2
@@ -17,6 +17,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -64,6 +65,9 @@ func TestCrcHashMod(t *testing.T) {
|
||||
// TestNewXL - tests initialization of all input disks
|
||||
// and constructs a valid `XL` object
|
||||
func TestNewXLSets(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var nDisks = 16 // Maximum disks.
|
||||
var erasureDisks []string
|
||||
for i := 0; i < nDisks; i++ {
|
||||
@@ -92,7 +96,7 @@ func TestNewXLSets(t *testing.T) {
|
||||
t.Fatalf("Unable to format disks for erasure, %s", err)
|
||||
}
|
||||
|
||||
if _, err := newXLSets(endpoints, storageDisks, format, 1, 16); err != nil {
|
||||
if _, err := newXLSets(ctx, endpoints, storageDisks, format, 1, 16); err != nil {
|
||||
t.Fatalf("Unable to initialize erasure")
|
||||
}
|
||||
}
|
||||
@@ -100,10 +104,13 @@ func TestNewXLSets(t *testing.T) {
|
||||
// TestHashedLayer - tests the hashed layer which will be returned
|
||||
// consistently for a given object name.
|
||||
func TestHashedLayer(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var objs []*xlObjects
|
||||
|
||||
for i := 0; i < 16; i++ {
|
||||
obj, fsDirs, err := prepareXL16()
|
||||
obj, fsDirs, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatal("Unable to initialize 'XL' object layer.", err)
|
||||
}
|
||||
|
||||
@@ -18,13 +18,17 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests for if parent directory is object
|
||||
func TestXLParentDirIsObject(t *testing.T) {
|
||||
obj, fsDisks, err := prepareXL16()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
obj, fsDisks, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to initialize 'XL' object layer.")
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -91,7 +92,10 @@ func TestCommonTime(t *testing.T) {
|
||||
// TestListOnlineDisks - checks if listOnlineDisks and outDatedDisks
|
||||
// are consistent with each other.
|
||||
func TestListOnlineDisks(t *testing.T) {
|
||||
obj, disks, err := prepareXL16()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
obj, disks, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Prepare XL backend failed - %v", err)
|
||||
}
|
||||
@@ -256,8 +260,9 @@ func TestListOnlineDisks(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDisksWithAllParts(t *testing.T) {
|
||||
ctx := GlobalContext
|
||||
obj, disks, err := prepareXL16()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
obj, disks, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Prepare XL backend failed - %v", err)
|
||||
}
|
||||
|
||||
+18
-9
@@ -122,11 +122,6 @@ func healBucket(ctx context.Context, storageDisks []StorageAPI, bucket string, w
|
||||
Endpoint: drive,
|
||||
State: beforeState[i],
|
||||
})
|
||||
res.After.Drives = append(res.After.Drives, madmin.HealDriveInfo{
|
||||
UUID: "",
|
||||
Endpoint: drive,
|
||||
State: afterState[i],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,12 +146,26 @@ func healBucket(ctx context.Context, storageDisks []StorageAPI, bucket string, w
|
||||
errs = g.Wait()
|
||||
|
||||
reducedErr = reduceWriteQuorumErrs(ctx, errs, bucketOpIgnoredErrs, writeQuorum)
|
||||
if reducedErr == errXLWriteQuorum {
|
||||
// Purge successfully created buckets if we don't have writeQuorum.
|
||||
undoMakeBucket(storageDisks, bucket)
|
||||
if reducedErr != nil {
|
||||
if reducedErr == errXLWriteQuorum {
|
||||
// Purge successfully created buckets if we don't have writeQuorum.
|
||||
undoMakeBucket(storageDisks, bucket)
|
||||
}
|
||||
return res, reducedErr
|
||||
}
|
||||
|
||||
return res, reducedErr
|
||||
for i := range afterState {
|
||||
if storageDisks[i] != nil {
|
||||
drive := storageDisks[i].String()
|
||||
res.After.Drives = append(res.After.Drives, madmin.HealDriveInfo{
|
||||
UUID: "",
|
||||
Endpoint: drive,
|
||||
State: afterState[i],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// listAllBuckets lists all buckets from all disks. It also
|
||||
|
||||
+38
-26
@@ -18,6 +18,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
@@ -26,6 +27,9 @@ import (
|
||||
|
||||
// Tests undoes and validates if the undoing completes successfully.
|
||||
func TestUndoMakeBucket(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
nDisks := 16
|
||||
fsDirs, err := getRandomDisks(nDisks)
|
||||
if err != nil {
|
||||
@@ -34,13 +38,13 @@ func TestUndoMakeBucket(t *testing.T) {
|
||||
defer removeRoots(fsDirs)
|
||||
|
||||
// Remove format.json on 16 disks.
|
||||
obj, _, err := initObjectLayer(mustGetZoneEndpoints(fsDirs...))
|
||||
obj, _, err := initObjectLayer(ctx, mustGetZoneEndpoints(fsDirs...))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bucketName := getRandomBucketName()
|
||||
if err = obj.MakeBucketWithLocation(GlobalContext, bucketName, ""); err != nil {
|
||||
if err = obj.MakeBucketWithLocation(ctx, bucketName, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
z := obj.(*xlZones)
|
||||
@@ -48,7 +52,7 @@ func TestUndoMakeBucket(t *testing.T) {
|
||||
undoMakeBucket(xl.getDisks(), bucketName)
|
||||
|
||||
// Validate if bucket was deleted properly.
|
||||
_, err = obj.GetBucketInfo(GlobalContext, bucketName)
|
||||
_, err = obj.GetBucketInfo(ctx, bucketName)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case BucketNotFound:
|
||||
@@ -59,8 +63,10 @@ func TestUndoMakeBucket(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHealObjectCorrupted(t *testing.T) {
|
||||
resetGlobalHealState()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
resetGlobalHealState()
|
||||
defer resetGlobalHealState()
|
||||
|
||||
nDisks := 16
|
||||
@@ -72,7 +78,7 @@ func TestHealObjectCorrupted(t *testing.T) {
|
||||
defer removeRoots(fsDirs)
|
||||
|
||||
// Everything is fine, should return nil
|
||||
objLayer, _, err := initObjectLayer(mustGetZoneEndpoints(fsDirs...))
|
||||
objLayer, _, err := initObjectLayer(ctx, mustGetZoneEndpoints(fsDirs...))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -82,21 +88,21 @@ func TestHealObjectCorrupted(t *testing.T) {
|
||||
data := bytes.Repeat([]byte("a"), 5*1024*1024)
|
||||
var opts ObjectOptions
|
||||
|
||||
err = objLayer.MakeBucketWithLocation(GlobalContext, bucket, "")
|
||||
err = objLayer.MakeBucketWithLocation(ctx, bucket, "")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to make a bucket - %v", err)
|
||||
}
|
||||
|
||||
// Create an object with multiple parts uploaded in decreasing
|
||||
// part number.
|
||||
uploadID, err := objLayer.NewMultipartUpload(GlobalContext, bucket, object, opts)
|
||||
uploadID, err := objLayer.NewMultipartUpload(ctx, bucket, object, opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a multipart upload - %v", err)
|
||||
}
|
||||
|
||||
var uploadedParts []CompletePart
|
||||
for _, partID := range []int{2, 1} {
|
||||
pInfo, err1 := objLayer.PutObjectPart(GlobalContext, bucket, object, uploadID, partID, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), opts)
|
||||
pInfo, err1 := objLayer.PutObjectPart(ctx, bucket, object, uploadID, partID, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), opts)
|
||||
if err1 != nil {
|
||||
t.Fatalf("Failed to upload a part - %v", err1)
|
||||
}
|
||||
@@ -106,7 +112,7 @@ func TestHealObjectCorrupted(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
_, err = objLayer.CompleteMultipartUpload(GlobalContext, bucket, object, uploadID, uploadedParts, ObjectOptions{})
|
||||
_, err = objLayer.CompleteMultipartUpload(ctx, bucket, object, uploadID, uploadedParts, ObjectOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to complete multipart upload - %v", err)
|
||||
}
|
||||
@@ -120,7 +126,7 @@ func TestHealObjectCorrupted(t *testing.T) {
|
||||
t.Fatalf("Failed to delete a file - %v", err)
|
||||
}
|
||||
|
||||
_, err = objLayer.HealObject(GlobalContext, bucket, object, madmin.HealOpts{ScanMode: madmin.HealNormalScan})
|
||||
_, err = objLayer.HealObject(ctx, bucket, object, madmin.HealOpts{ScanMode: madmin.HealNormalScan})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to heal object - %v", err)
|
||||
}
|
||||
@@ -143,7 +149,7 @@ func TestHealObjectCorrupted(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Failure during creating part.1 - %v", err)
|
||||
}
|
||||
_, err = objLayer.HealObject(GlobalContext, bucket, object, madmin.HealOpts{DryRun: false, Remove: true, ScanMode: madmin.HealDeepScan})
|
||||
_, err = objLayer.HealObject(ctx, bucket, object, madmin.HealOpts{DryRun: false, Remove: true, ScanMode: madmin.HealDeepScan})
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil but received %v", err)
|
||||
}
|
||||
@@ -169,7 +175,7 @@ func TestHealObjectCorrupted(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Failure during creating part.1 - %v", err)
|
||||
}
|
||||
_, err = objLayer.HealObject(GlobalContext, bucket, object, madmin.HealOpts{DryRun: false, Remove: true, ScanMode: madmin.HealDeepScan})
|
||||
_, err = objLayer.HealObject(ctx, bucket, object, madmin.HealOpts{DryRun: false, Remove: true, ScanMode: madmin.HealDeepScan})
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil but received %v", err)
|
||||
}
|
||||
@@ -189,7 +195,7 @@ func TestHealObjectCorrupted(t *testing.T) {
|
||||
}
|
||||
|
||||
// Try healing now, expect to receive errFileNotFound.
|
||||
_, err = objLayer.HealObject(GlobalContext, bucket, object, madmin.HealOpts{DryRun: false, Remove: true, ScanMode: madmin.HealDeepScan})
|
||||
_, err = objLayer.HealObject(ctx, bucket, object, madmin.HealOpts{DryRun: false, Remove: true, ScanMode: madmin.HealDeepScan})
|
||||
if err != nil {
|
||||
if _, ok := err.(ObjectNotFound); !ok {
|
||||
t.Errorf("Expect %v but received %v", ObjectNotFound{Bucket: bucket, Object: object}, err)
|
||||
@@ -197,7 +203,7 @@ func TestHealObjectCorrupted(t *testing.T) {
|
||||
}
|
||||
|
||||
// since majority of xl.jsons are not available, object should be successfully deleted.
|
||||
_, err = objLayer.GetObjectInfo(GlobalContext, bucket, object, ObjectOptions{})
|
||||
_, err = objLayer.GetObjectInfo(ctx, bucket, object, ObjectOptions{})
|
||||
if _, ok := err.(ObjectNotFound); !ok {
|
||||
t.Errorf("Expect %v but received %v", ObjectNotFound{Bucket: bucket, Object: object}, err)
|
||||
}
|
||||
@@ -205,6 +211,9 @@ func TestHealObjectCorrupted(t *testing.T) {
|
||||
|
||||
// Tests healing of object.
|
||||
func TestHealObjectXL(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
nDisks := 16
|
||||
fsDirs, err := getRandomDisks(nDisks)
|
||||
if err != nil {
|
||||
@@ -214,7 +223,7 @@ func TestHealObjectXL(t *testing.T) {
|
||||
defer removeRoots(fsDirs)
|
||||
|
||||
// Everything is fine, should return nil
|
||||
obj, _, err := initObjectLayer(mustGetZoneEndpoints(fsDirs...))
|
||||
obj, _, err := initObjectLayer(ctx, mustGetZoneEndpoints(fsDirs...))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -224,21 +233,21 @@ func TestHealObjectXL(t *testing.T) {
|
||||
data := bytes.Repeat([]byte("a"), 5*1024*1024)
|
||||
var opts ObjectOptions
|
||||
|
||||
err = obj.MakeBucketWithLocation(GlobalContext, bucket, "")
|
||||
err = obj.MakeBucketWithLocation(ctx, bucket, "")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to make a bucket - %v", err)
|
||||
}
|
||||
|
||||
// Create an object with multiple parts uploaded in decreasing
|
||||
// part number.
|
||||
uploadID, err := obj.NewMultipartUpload(GlobalContext, bucket, object, opts)
|
||||
uploadID, err := obj.NewMultipartUpload(ctx, bucket, object, opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a multipart upload - %v", err)
|
||||
}
|
||||
|
||||
var uploadedParts []CompletePart
|
||||
for _, partID := range []int{2, 1} {
|
||||
pInfo, err1 := obj.PutObjectPart(GlobalContext, bucket, object, uploadID, partID, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), opts)
|
||||
pInfo, err1 := obj.PutObjectPart(ctx, bucket, object, uploadID, partID, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), opts)
|
||||
if err1 != nil {
|
||||
t.Fatalf("Failed to upload a part - %v", err1)
|
||||
}
|
||||
@@ -248,7 +257,7 @@ func TestHealObjectXL(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
_, err = obj.CompleteMultipartUpload(GlobalContext, bucket, object, uploadID, uploadedParts, ObjectOptions{})
|
||||
_, err = obj.CompleteMultipartUpload(ctx, bucket, object, uploadID, uploadedParts, ObjectOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to complete multipart upload - %v", err)
|
||||
}
|
||||
@@ -262,7 +271,7 @@ func TestHealObjectXL(t *testing.T) {
|
||||
t.Fatalf("Failed to delete a file - %v", err)
|
||||
}
|
||||
|
||||
_, err = obj.HealObject(GlobalContext, bucket, object, madmin.HealOpts{ScanMode: madmin.HealNormalScan})
|
||||
_, err = obj.HealObject(ctx, bucket, object, madmin.HealOpts{ScanMode: madmin.HealNormalScan})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to heal object - %v", err)
|
||||
}
|
||||
@@ -284,7 +293,7 @@ func TestHealObjectXL(t *testing.T) {
|
||||
z.zones[0].xlDisksMu.Unlock()
|
||||
|
||||
// Try healing now, expect to receive errDiskNotFound.
|
||||
_, err = obj.HealObject(GlobalContext, bucket, object, madmin.HealOpts{ScanMode: madmin.HealDeepScan})
|
||||
_, err = obj.HealObject(ctx, bucket, object, madmin.HealOpts{ScanMode: madmin.HealDeepScan})
|
||||
// since majority of xl.jsons are not available, object quorum can't be read properly and error will be errXLReadQuorum
|
||||
if _, ok := err.(InsufficientReadQuorum); !ok {
|
||||
t.Errorf("Expected %v but received %v", InsufficientReadQuorum{}, err)
|
||||
@@ -293,6 +302,9 @@ func TestHealObjectXL(t *testing.T) {
|
||||
|
||||
// Tests healing of empty directories
|
||||
func TestHealEmptyDirectoryXL(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
nDisks := 16
|
||||
fsDirs, err := getRandomDisks(nDisks)
|
||||
if err != nil {
|
||||
@@ -301,7 +313,7 @@ func TestHealEmptyDirectoryXL(t *testing.T) {
|
||||
defer removeRoots(fsDirs)
|
||||
|
||||
// Everything is fine, should return nil
|
||||
obj, _, err := initObjectLayer(mustGetZoneEndpoints(fsDirs...))
|
||||
obj, _, err := initObjectLayer(ctx, mustGetZoneEndpoints(fsDirs...))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -310,13 +322,13 @@ func TestHealEmptyDirectoryXL(t *testing.T) {
|
||||
object := "empty-dir/"
|
||||
var opts ObjectOptions
|
||||
|
||||
err = obj.MakeBucketWithLocation(GlobalContext, bucket, "")
|
||||
err = obj.MakeBucketWithLocation(ctx, bucket, "")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to make a bucket - %v", err)
|
||||
}
|
||||
|
||||
// Upload an empty directory
|
||||
_, err = obj.PutObject(GlobalContext, bucket, object, mustGetPutObjReader(t,
|
||||
_, err = obj.PutObject(ctx, bucket, object, mustGetPutObjReader(t,
|
||||
bytes.NewReader([]byte{}), 0, "", ""), opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -332,7 +344,7 @@ func TestHealEmptyDirectoryXL(t *testing.T) {
|
||||
}
|
||||
|
||||
// Heal the object
|
||||
hr, err := obj.HealObject(GlobalContext, bucket, object, madmin.HealOpts{ScanMode: madmin.HealNormalScan})
|
||||
hr, err := obj.HealObject(ctx, bucket, object, madmin.HealOpts{ScanMode: madmin.HealNormalScan})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to heal object - %v", err)
|
||||
}
|
||||
@@ -356,7 +368,7 @@ func TestHealEmptyDirectoryXL(t *testing.T) {
|
||||
}
|
||||
|
||||
// Heal the same object again
|
||||
hr, err = obj.HealObject(GlobalContext, bucket, object, madmin.HealOpts{ScanMode: madmin.HealNormalScan})
|
||||
hr, err = obj.HealObject(ctx, bucket, object, madmin.HealOpts{ScanMode: madmin.HealNormalScan})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to heal object - %v", err)
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ func (xl xlObjects) listObjects(ctx context.Context, bucket, prefix, marker, del
|
||||
} else {
|
||||
// Set the Mode to a "regular" file.
|
||||
var err error
|
||||
objInfo, err = xl.getObjectInfo(ctx, bucket, entry)
|
||||
objInfo, err = xl.getObjectInfo(ctx, bucket, entry, ObjectOptions{})
|
||||
if err != nil {
|
||||
// Ignore errFileNotFound as the object might have got
|
||||
// deleted in the interim period of listing and getObjectInfo(),
|
||||
|
||||
@@ -50,7 +50,7 @@ func (xl xlObjects) getMultipartSHADir(bucket, object string) string {
|
||||
|
||||
// checkUploadIDExists - verify if a given uploadID exists and is valid.
|
||||
func (xl xlObjects) checkUploadIDExists(ctx context.Context, bucket, object, uploadID string) error {
|
||||
_, err := xl.getObjectInfo(ctx, minioMetaMultipartBucket, xl.getUploadIDDir(bucket, object, uploadID))
|
||||
_, err := xl.getObjectInfo(ctx, minioMetaMultipartBucket, xl.getUploadIDDir(bucket, object, uploadID), ObjectOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -675,7 +675,7 @@ func (xl xlObjects) CompleteMultipartUpload(ctx context.Context, bucket string,
|
||||
if xl.isObject(bucket, object) {
|
||||
// Deny if WORM is enabled
|
||||
if isWORMEnabled(bucket) {
|
||||
if _, err := xl.getObjectInfo(ctx, bucket, object); err == nil {
|
||||
if _, err := xl.getObjectInfo(ctx, bucket, object, ObjectOptions{}); err == nil {
|
||||
return ObjectInfo{}, ObjectAlreadyExists{Bucket: bucket, Object: object}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,11 @@ import (
|
||||
|
||||
// Tests cleanup multipart uploads for erasure coded backend.
|
||||
func TestXLCleanupStaleMultipartUploads(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Create an instance of xl backend
|
||||
obj, fsDirs, err := prepareXL16()
|
||||
obj, fsDirs, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -40,15 +43,12 @@ func TestXLCleanupStaleMultipartUploads(t *testing.T) {
|
||||
objectName := "object"
|
||||
var opts ObjectOptions
|
||||
|
||||
obj.MakeBucketWithLocation(GlobalContext, bucketName, "")
|
||||
obj.MakeBucketWithLocation(ctx, bucketName, "")
|
||||
uploadID, err := obj.NewMultipartUpload(GlobalContext, bucketName, objectName, opts)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected err: ", err)
|
||||
}
|
||||
|
||||
// Create a context we can cancel.
|
||||
ctx, cancel := context.WithCancel(GlobalContext)
|
||||
|
||||
var cleanupWg sync.WaitGroup
|
||||
cleanupWg.Add(1)
|
||||
go func() {
|
||||
@@ -65,7 +65,7 @@ func TestXLCleanupStaleMultipartUploads(t *testing.T) {
|
||||
cleanupWg.Wait()
|
||||
|
||||
// Check if upload id was already purged.
|
||||
if err = obj.AbortMultipartUpload(GlobalContext, bucketName, objectName, uploadID); err != nil {
|
||||
if err = obj.AbortMultipartUpload(context.Background(), bucketName, objectName, uploadID); err != nil {
|
||||
if _, ok := err.(InvalidUploadID); !ok {
|
||||
t.Fatal("Unexpected err: ", err)
|
||||
}
|
||||
|
||||
+7
-6
@@ -137,16 +137,16 @@ func (xl xlObjects) GetObjectNInfo(ctx context.Context, bucket, object string, r
|
||||
if objInfo, err = xl.getObjectInfoDir(ctx, bucket, object); err != nil {
|
||||
return nil, toObjectErr(err, bucket, object)
|
||||
}
|
||||
return NewGetObjectReaderFromReader(bytes.NewBuffer(nil), objInfo, opts.CheckCopyPrecondFn)
|
||||
return NewGetObjectReaderFromReader(bytes.NewBuffer(nil), objInfo, opts)
|
||||
}
|
||||
|
||||
var objInfo ObjectInfo
|
||||
objInfo, err = xl.getObjectInfo(ctx, bucket, object)
|
||||
objInfo, err = xl.getObjectInfo(ctx, bucket, object, opts)
|
||||
if err != nil {
|
||||
return nil, toObjectErr(err, bucket, object)
|
||||
}
|
||||
|
||||
fn, off, length, nErr := NewGetObjectReader(rs, objInfo, opts.CheckCopyPrecondFn)
|
||||
fn, off, length, nErr := NewGetObjectReader(rs, objInfo, opts)
|
||||
if nErr != nil {
|
||||
return nil, nErr
|
||||
}
|
||||
@@ -156,6 +156,7 @@ func (xl xlObjects) GetObjectNInfo(ctx context.Context, bucket, object string, r
|
||||
err := xl.getObject(ctx, bucket, object, off, length, pw, "", opts)
|
||||
pw.CloseWithError(err)
|
||||
}()
|
||||
|
||||
// Cleanup function to cause the go routine above to exit, in
|
||||
// case of incomplete read.
|
||||
pipeCloser := func() { pr.Close() }
|
||||
@@ -365,7 +366,7 @@ func (xl xlObjects) GetObjectInfo(ctx context.Context, bucket, object string, op
|
||||
return info, nil
|
||||
}
|
||||
|
||||
info, err := xl.getObjectInfo(ctx, bucket, object)
|
||||
info, err := xl.getObjectInfo(ctx, bucket, object, opts)
|
||||
if err != nil {
|
||||
return oi, toObjectErr(err, bucket, object)
|
||||
}
|
||||
@@ -374,7 +375,7 @@ func (xl xlObjects) GetObjectInfo(ctx context.Context, bucket, object string, op
|
||||
}
|
||||
|
||||
// getObjectInfo - wrapper for reading object metadata and constructs ObjectInfo.
|
||||
func (xl xlObjects) getObjectInfo(ctx context.Context, bucket, object string) (objInfo ObjectInfo, err error) {
|
||||
func (xl xlObjects) getObjectInfo(ctx context.Context, bucket, object string, opt ObjectOptions) (objInfo ObjectInfo, err error) {
|
||||
disks := xl.getDisks()
|
||||
|
||||
// Read metadata associated with the object from all disks.
|
||||
@@ -629,7 +630,7 @@ func (xl xlObjects) putObject(ctx context.Context, bucket string, object string,
|
||||
if xl.isObject(bucket, object) {
|
||||
// Deny if WORM is enabled
|
||||
if isWORMEnabled(bucket) {
|
||||
if _, err := xl.getObjectInfo(ctx, bucket, object); err == nil {
|
||||
if _, err := xl.getObjectInfo(ctx, bucket, object, ObjectOptions{}); err == nil {
|
||||
return ObjectInfo{}, ObjectAlreadyExists{Bucket: bucket, Object: object}
|
||||
}
|
||||
}
|
||||
|
||||
+55
-35
@@ -18,6 +18,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
@@ -32,12 +33,15 @@ import (
|
||||
)
|
||||
|
||||
func TestRepeatPutObjectPart(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var objLayer ObjectLayer
|
||||
var disks []string
|
||||
var err error
|
||||
var opts ObjectOptions
|
||||
|
||||
objLayer, disks, err = prepareXL16()
|
||||
objLayer, disks, err = prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -45,23 +49,23 @@ func TestRepeatPutObjectPart(t *testing.T) {
|
||||
// cleaning up of temporary test directories
|
||||
defer removeRoots(disks)
|
||||
|
||||
err = objLayer.MakeBucketWithLocation(GlobalContext, "bucket1", "")
|
||||
err = objLayer.MakeBucketWithLocation(ctx, "bucket1", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
uploadID, err := objLayer.NewMultipartUpload(GlobalContext, "bucket1", "mpartObj1", opts)
|
||||
uploadID, err := objLayer.NewMultipartUpload(ctx, "bucket1", "mpartObj1", opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fiveMBBytes := bytes.Repeat([]byte("a"), 5*humanize.MiByte)
|
||||
md5Hex := getMD5Hash(fiveMBBytes)
|
||||
_, err = objLayer.PutObjectPart(GlobalContext, "bucket1", "mpartObj1", uploadID, 1, mustGetPutObjReader(t, bytes.NewReader(fiveMBBytes), 5*humanize.MiByte, md5Hex, ""), opts)
|
||||
_, err = objLayer.PutObjectPart(ctx, "bucket1", "mpartObj1", uploadID, 1, mustGetPutObjReader(t, bytes.NewReader(fiveMBBytes), 5*humanize.MiByte, md5Hex, ""), opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// PutObjectPart should succeed even if part already exists. ref: https://github.com/minio/minio/issues/1930
|
||||
_, err = objLayer.PutObjectPart(GlobalContext, "bucket1", "mpartObj1", uploadID, 1, mustGetPutObjReader(t, bytes.NewReader(fiveMBBytes), 5*humanize.MiByte, md5Hex, ""), opts)
|
||||
_, err = objLayer.PutObjectPart(ctx, "bucket1", "mpartObj1", uploadID, 1, mustGetPutObjReader(t, bytes.NewReader(fiveMBBytes), 5*humanize.MiByte, md5Hex, ""), opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -83,24 +87,27 @@ func TestXLDeleteObjectBasic(t *testing.T) {
|
||||
{"bucket", "dir/obj", nil},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Create an instance of xl backend
|
||||
xl, fsDirs, err := prepareXL16()
|
||||
xl, fsDirs, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = xl.MakeBucketWithLocation(GlobalContext, "bucket", "")
|
||||
err = xl.MakeBucketWithLocation(ctx, "bucket", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create object "dir/obj" under bucket "bucket" for Test 7 to pass
|
||||
_, err = xl.PutObject(GlobalContext, "bucket", "dir/obj", mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), ObjectOptions{})
|
||||
_, err = xl.PutObject(ctx, "bucket", "dir/obj", mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), ObjectOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("XL Object upload failed: <ERROR> %s", err)
|
||||
}
|
||||
for i, test := range testCases {
|
||||
actualErr := xl.DeleteObject(GlobalContext, test.bucket, test.object)
|
||||
actualErr := xl.DeleteObject(ctx, test.bucket, test.object)
|
||||
if test.expectedErr != nil && actualErr != test.expectedErr {
|
||||
t.Errorf("Test %d: Expected to fail with %s, but failed with %s", i+1, test.expectedErr, actualErr)
|
||||
}
|
||||
@@ -113,10 +120,11 @@ func TestXLDeleteObjectBasic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestXLDeleteObjectsXLSet(t *testing.T) {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
var objs []*xlObjects
|
||||
for i := 0; i < 32; i++ {
|
||||
obj, fsDirs, err := prepareXL(16)
|
||||
obj, fsDirs, err := prepareXL(ctx, 16)
|
||||
if err != nil {
|
||||
t.Fatal("Unable to initialize 'XL' object layer.", err)
|
||||
}
|
||||
@@ -188,8 +196,11 @@ func TestXLDeleteObjectsXLSet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestXLDeleteObjectDiskNotFound(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Create an instance of xl backend.
|
||||
obj, fsDirs, err := prepareXL16()
|
||||
obj, fsDirs, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -200,7 +211,7 @@ func TestXLDeleteObjectDiskNotFound(t *testing.T) {
|
||||
xl := z.zones[0].sets[0]
|
||||
|
||||
// Create "bucket"
|
||||
err = obj.MakeBucketWithLocation(GlobalContext, "bucket", "")
|
||||
err = obj.MakeBucketWithLocation(ctx, "bucket", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -209,7 +220,7 @@ func TestXLDeleteObjectDiskNotFound(t *testing.T) {
|
||||
object := "object"
|
||||
opts := ObjectOptions{}
|
||||
// Create object "obj" under bucket "bucket".
|
||||
_, err = obj.PutObject(GlobalContext, bucket, object, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), opts)
|
||||
_, err = obj.PutObject(ctx, bucket, object, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -224,13 +235,13 @@ func TestXLDeleteObjectDiskNotFound(t *testing.T) {
|
||||
return xlDisks
|
||||
}
|
||||
z.zones[0].xlDisksMu.Unlock()
|
||||
err = obj.DeleteObject(GlobalContext, bucket, object)
|
||||
err = obj.DeleteObject(ctx, bucket, object)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create "obj" under "bucket".
|
||||
_, err = obj.PutObject(GlobalContext, bucket, object, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), opts)
|
||||
_, err = obj.PutObject(ctx, bucket, object, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -244,7 +255,7 @@ func TestXLDeleteObjectDiskNotFound(t *testing.T) {
|
||||
return xlDisks
|
||||
}
|
||||
z.zones[0].xlDisksMu.Unlock()
|
||||
err = obj.DeleteObject(GlobalContext, bucket, object)
|
||||
err = obj.DeleteObject(ctx, bucket, object)
|
||||
// since majority of disks are not available, metaquorum is not achieved and hence errXLReadQuorum error
|
||||
if err != toObjectErr(errXLReadQuorum, bucket, object) {
|
||||
t.Errorf("Expected deleteObject to fail with %v, but failed with %v", toObjectErr(errXLReadQuorum, bucket, object), err)
|
||||
@@ -252,8 +263,11 @@ func TestXLDeleteObjectDiskNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetObjectNoQuorum(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Create an instance of xl backend.
|
||||
obj, fsDirs, err := prepareXL16()
|
||||
obj, fsDirs, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -264,7 +278,7 @@ func TestGetObjectNoQuorum(t *testing.T) {
|
||||
xl := z.zones[0].sets[0]
|
||||
|
||||
// Create "bucket"
|
||||
err = obj.MakeBucketWithLocation(GlobalContext, "bucket", "")
|
||||
err = obj.MakeBucketWithLocation(ctx, "bucket", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -273,7 +287,7 @@ func TestGetObjectNoQuorum(t *testing.T) {
|
||||
object := "object"
|
||||
opts := ObjectOptions{}
|
||||
// Create "object" under "bucket".
|
||||
_, err = obj.PutObject(GlobalContext, bucket, object, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), opts)
|
||||
_, err = obj.PutObject(ctx, bucket, object, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -302,7 +316,7 @@ func TestGetObjectNoQuorum(t *testing.T) {
|
||||
}
|
||||
z.zones[0].xlDisksMu.Unlock()
|
||||
// Fetch object from store.
|
||||
err = xl.GetObject(GlobalContext, bucket, object, 0, int64(len("abcd")), ioutil.Discard, "", opts)
|
||||
err = xl.GetObject(ctx, bucket, object, 0, int64(len("abcd")), ioutil.Discard, "", opts)
|
||||
if err != toObjectErr(errXLReadQuorum, bucket, object) {
|
||||
t.Errorf("Expected putObject to fail with %v, but failed with %v", toObjectErr(errXLWriteQuorum, bucket, object), err)
|
||||
}
|
||||
@@ -310,8 +324,11 @@ func TestGetObjectNoQuorum(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutObjectNoQuorum(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Create an instance of xl backend.
|
||||
obj, fsDirs, err := prepareXL16()
|
||||
obj, fsDirs, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -323,7 +340,7 @@ func TestPutObjectNoQuorum(t *testing.T) {
|
||||
xl := z.zones[0].sets[0]
|
||||
|
||||
// Create "bucket"
|
||||
err = obj.MakeBucketWithLocation(GlobalContext, "bucket", "")
|
||||
err = obj.MakeBucketWithLocation(ctx, "bucket", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -332,7 +349,7 @@ func TestPutObjectNoQuorum(t *testing.T) {
|
||||
object := "object"
|
||||
opts := ObjectOptions{}
|
||||
// Create "object" under "bucket".
|
||||
_, err = obj.PutObject(GlobalContext, bucket, object, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), opts)
|
||||
_, err = obj.PutObject(ctx, bucket, object, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -361,7 +378,7 @@ func TestPutObjectNoQuorum(t *testing.T) {
|
||||
}
|
||||
z.zones[0].xlDisksMu.Unlock()
|
||||
// Upload new content to same object "object"
|
||||
_, err = obj.PutObject(GlobalContext, bucket, object, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), opts)
|
||||
_, err = obj.PutObject(ctx, bucket, object, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), opts)
|
||||
if err != toObjectErr(errXLWriteQuorum, bucket, object) {
|
||||
t.Errorf("Expected putObject to fail with %v, but failed with %v", toObjectErr(errXLWriteQuorum, bucket, object), err)
|
||||
}
|
||||
@@ -370,7 +387,10 @@ func TestPutObjectNoQuorum(t *testing.T) {
|
||||
|
||||
// Tests both object and bucket healing.
|
||||
func TestHealing(t *testing.T) {
|
||||
obj, fsDirs, err := prepareXL16()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
obj, fsDirs, err := prepareXL16(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -380,7 +400,7 @@ func TestHealing(t *testing.T) {
|
||||
xl := z.zones[0].sets[0]
|
||||
|
||||
// Create "bucket"
|
||||
err = obj.MakeBucketWithLocation(GlobalContext, "bucket", "")
|
||||
err = obj.MakeBucketWithLocation(ctx, "bucket", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -395,13 +415,13 @@ func TestHealing(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = obj.PutObject(GlobalContext, bucket, object, mustGetPutObjReader(t, bytes.NewReader(data), length, "", ""), ObjectOptions{})
|
||||
_, err = obj.PutObject(ctx, bucket, object, mustGetPutObjReader(t, bytes.NewReader(data), length, "", ""), ObjectOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
disk := xl.getDisks()[0]
|
||||
xlMetaPreHeal, err := readXLMeta(GlobalContext, disk, bucket, object)
|
||||
xlMetaPreHeal, err := readXLMeta(ctx, disk, bucket, object)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -413,12 +433,12 @@ func TestHealing(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = xl.HealObject(GlobalContext, bucket, object, madmin.HealOpts{ScanMode: madmin.HealNormalScan})
|
||||
_, err = xl.HealObject(ctx, bucket, object, madmin.HealOpts{ScanMode: madmin.HealNormalScan})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
xlMetaPostHeal, err := readXLMeta(GlobalContext, disk, bucket, object)
|
||||
xlMetaPostHeal, err := readXLMeta(ctx, disk, bucket, object)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -437,17 +457,17 @@ func TestHealing(t *testing.T) {
|
||||
// gone down when an object was replaced by a new object.
|
||||
xlMetaOutDated := xlMetaPreHeal
|
||||
xlMetaOutDated.Stat.ModTime = time.Now()
|
||||
err = writeXLMetadata(GlobalContext, disk, bucket, object, xlMetaOutDated)
|
||||
err = writeXLMetadata(ctx, disk, bucket, object, xlMetaOutDated)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = xl.HealObject(GlobalContext, bucket, object, madmin.HealOpts{ScanMode: madmin.HealDeepScan})
|
||||
_, err = xl.HealObject(ctx, bucket, object, madmin.HealOpts{ScanMode: madmin.HealDeepScan})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
xlMetaPostHeal, err = readXLMeta(GlobalContext, disk, bucket, object)
|
||||
xlMetaPostHeal, err = readXLMeta(ctx, disk, bucket, object)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -464,7 +484,7 @@ func TestHealing(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// This would create the bucket.
|
||||
_, err = xl.HealBucket(GlobalContext, bucket, false, false)
|
||||
_, err = xl.HealBucket(ctx, bucket, false, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
@@ -383,12 +384,15 @@ func TestGetPartSizeFromIdx(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestShuffleDisks(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
nDisks := 16
|
||||
disks, err := getRandomDisks(nDisks)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
objLayer, _, err := initObjectLayer(mustGetZoneEndpoints(disks...))
|
||||
objLayer, _, err := initObjectLayer(ctx, mustGetZoneEndpoints(disks...))
|
||||
if err != nil {
|
||||
removeRoots(disks)
|
||||
t.Fatal(err)
|
||||
@@ -428,12 +432,15 @@ func testShuffleDisks(t *testing.T, z *xlZones) {
|
||||
|
||||
// TestEvalDisks tests the behavior of evalDisks
|
||||
func TestEvalDisks(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
nDisks := 16
|
||||
disks, err := getRandomDisks(nDisks)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
objLayer, _, err := initObjectLayer(mustGetZoneEndpoints(disks...))
|
||||
objLayer, _, err := initObjectLayer(ctx, mustGetZoneEndpoints(disks...))
|
||||
if err != nil {
|
||||
removeRoots(disks)
|
||||
t.Fatal(err)
|
||||
|
||||
+3
-3
@@ -55,7 +55,7 @@ func (z *xlZones) quickHealBuckets(ctx context.Context) {
|
||||
}
|
||||
|
||||
// Initialize new zone of erasure sets.
|
||||
func newXLZones(endpointZones EndpointZones) (ObjectLayer, error) {
|
||||
func newXLZones(ctx context.Context, endpointZones EndpointZones) (ObjectLayer, error) {
|
||||
var (
|
||||
deploymentID string
|
||||
err error
|
||||
@@ -74,13 +74,13 @@ func newXLZones(endpointZones EndpointZones) (ObjectLayer, error) {
|
||||
if deploymentID == "" {
|
||||
deploymentID = formats[i].ID
|
||||
}
|
||||
z.zones[i], err = newXLSets(ep.Endpoints, storageDisks[i], formats[i], ep.SetCount, ep.DrivesPerSet)
|
||||
z.zones[i], err = newXLSets(ctx, ep.Endpoints, storageDisks[i], formats[i], ep.SetCount, ep.DrivesPerSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if !z.SingleZone() {
|
||||
z.quickHealBuckets(GlobalContext)
|
||||
z.quickHealBuckets(ctx)
|
||||
}
|
||||
return z, nil
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ internal (on|off) set to 'on' for exchange to be not used directly by p
|
||||
auto_deleted (on|off) auto delete queue when set to 'on', when there are no consumers
|
||||
delivery_mode (number) set to '1' for non-persistent or '2' for persistent queue
|
||||
queue_dir (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
comment (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
@@ -92,11 +92,11 @@ MINIO_NOTIFY_AMQP_INTERNAL (on|off) set to 'on' for exchange to be not
|
||||
MINIO_NOTIFY_AMQP_AUTO_DELETED (on|off) auto delete queue when set to 'on', when there are no consumers
|
||||
MINIO_NOTIFY_AMQP_DELIVERY_MODE (number) set to '1' for non-persistent or '2' for persistent queue
|
||||
MINIO_NOTIFY_AMQP_QUEUE_DIR (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
MINIO_NOTIFY_AMQP_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
MINIO_NOTIFY_AMQP_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
MINIO_NOTIFY_AMQP_COMMENT (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
MinIO supports persistent event store. The persistent store will backup events when the AMQP broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 10000.
|
||||
MinIO supports persistent event store. The persistent store will backup events when the AMQP broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 100000.
|
||||
|
||||
To update the configuration, use `mc admin config get notify_amqp` command to get the current configuration for `notify_amqp`.
|
||||
|
||||
@@ -203,7 +203,7 @@ qos (number) set the quality of service priority, defaults t
|
||||
keep_alive_interval (duration) keep-alive interval for MQTT connections in s,m,h,d
|
||||
reconnect_interval (duration) reconnect interval for MQTT connections in s,m,h,d
|
||||
queue_dir (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
comment (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
@@ -223,11 +223,11 @@ MINIO_NOTIFY_MQTT_QOS (number) set the quality of service pr
|
||||
MINIO_NOTIFY_MQTT_KEEP_ALIVE_INTERVAL (duration) keep-alive interval for MQTT connections in s,m,h,d
|
||||
MINIO_NOTIFY_MQTT_RECONNECT_INTERVAL (duration) reconnect interval for MQTT connections in s,m,h,d
|
||||
MINIO_NOTIFY_MQTT_QUEUE_DIR (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
MINIO_NOTIFY_MQTT_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
MINIO_NOTIFY_MQTT_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
MINIO_NOTIFY_MQTT_COMMENT (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
MinIO supports persistent event store. The persistent store will backup events when the MQTT broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 10000.
|
||||
MinIO supports persistent event store. The persistent store will backup events when the MQTT broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 100000.
|
||||
|
||||
To update the configuration, use `mc admin config get` command to get the current configuration.
|
||||
|
||||
@@ -336,7 +336,7 @@ url* (url) Elasticsearch server's address, with optional
|
||||
index* (string) Elasticsearch index to store/update events, index is auto-created
|
||||
format* (namespace*|access) 'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'
|
||||
queue_dir (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
comment (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
@@ -352,13 +352,13 @@ MINIO_NOTIFY_ELASTICSEARCH_URL* (url) Elasticsearch serve
|
||||
MINIO_NOTIFY_ELASTICSEARCH_INDEX* (string) Elasticsearch index to store/update events, index is auto-created
|
||||
MINIO_NOTIFY_ELASTICSEARCH_FORMAT* (namespace*|access) 'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'
|
||||
MINIO_NOTIFY_ELASTICSEARCH_QUEUE_DIR (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
MINIO_NOTIFY_ELASTICSEARCH_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
MINIO_NOTIFY_ELASTICSEARCH_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
MINIO_NOTIFY_ELASTICSEARCH_COMMENT (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
For example: `http://localhost:9200` or with authentication info `http://elastic:MagicWord@127.0.0.1:9200`.
|
||||
|
||||
MinIO supports persistent event store. The persistent store will backup events when the Elasticsearch broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 10000.
|
||||
MinIO supports persistent event store. The persistent store will backup events when the Elasticsearch broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 100000.
|
||||
|
||||
If Elasticsearch has authentication enabled, the credentials can be supplied to MinIO via the `url` parameter formatted as `PROTO://USERNAME:PASSWORD@ELASTICSEARCH_HOST:PORT`.
|
||||
|
||||
@@ -502,7 +502,7 @@ key* (string) Redis key to store/update events, key is auto-
|
||||
format* (namespace*|access) 'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'
|
||||
password (string) Redis server password
|
||||
queue_dir (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
comment (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
@@ -518,11 +518,11 @@ MINIO_NOTIFY_REDIS_KEY* (string) Redis key to store/update e
|
||||
MINIO_NOTIFY_REDIS_FORMAT* (namespace*|access) 'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'
|
||||
MINIO_NOTIFY_REDIS_PASSWORD (string) Redis server password
|
||||
MINIO_NOTIFY_REDIS_QUEUE_DIR (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
MINIO_NOTIFY_REDIS_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
MINIO_NOTIFY_REDIS_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
MINIO_NOTIFY_REDIS_COMMENT (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
MinIO supports persistent event store. The persistent store will backup events when the Redis broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 10000.
|
||||
MinIO supports persistent event store. The persistent store will backup events when the Redis broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 100000.
|
||||
|
||||
To update the configuration, use `mc admin config get` command to get the current configuration.
|
||||
|
||||
@@ -591,7 +591,7 @@ Install NATS from [here](http://nats.io/).
|
||||
|
||||
### Step 1: Add NATS endpoint to MinIO
|
||||
|
||||
MinIO supports persistent event store. The persistent store will backup events when the NATS broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 10000.
|
||||
MinIO supports persistent event store. The persistent store will backup events when the NATS broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 100000.
|
||||
|
||||
```
|
||||
KEY:
|
||||
@@ -614,7 +614,7 @@ cert_authority (string) path to certificate chain of the t
|
||||
client_cert (string) client cert for NATS mTLS auth
|
||||
client_key (string) client cert key for NATS mTLS auth
|
||||
queue_dir (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
comment (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
@@ -641,7 +641,7 @@ MINIO_NOTIFY_NATS_CERT_AUTHORITY (string) path to certific
|
||||
MINIO_NOTIFY_NATS_CLIENT_CERT (string) client cert for NATS mTLS auth
|
||||
MINIO_NOTIFY_NATS_CLIENT_KEY (string) client cert key for NATS mTLS auth
|
||||
MINIO_NOTIFY_NATS_QUEUE_DIR (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
MINIO_NOTIFY_NATS_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
MINIO_NOTIFY_NATS_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
MINIO_NOTIFY_NATS_COMMENT (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
@@ -856,7 +856,7 @@ connection_string* (string) Postgres server connection-string e.g.
|
||||
table* (string) DB table name to store/update events, table is auto-created
|
||||
format* (namespace*|access) 'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'
|
||||
queue_dir (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
comment (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
@@ -871,11 +871,11 @@ MINIO_NOTIFY_POSTGRES_CONNECTION_STRING* (string) Postgres server c
|
||||
MINIO_NOTIFY_POSTGRES_TABLE* (string) DB table name to store/update events, table is auto-created
|
||||
MINIO_NOTIFY_POSTGRES_FORMAT* (namespace*|access) 'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'
|
||||
MINIO_NOTIFY_POSTGRES_QUEUE_DIR (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
MINIO_NOTIFY_POSTGRES_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
MINIO_NOTIFY_POSTGRES_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
MINIO_NOTIFY_POSTGRES_COMMENT (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
MinIO supports persistent event store. The persistent store will backup events when the PostgreSQL connection goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 10000.
|
||||
MinIO supports persistent event store. The persistent store will backup events when the PostgreSQL connection goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 100000.
|
||||
|
||||
Note that for illustration here, we have disabled SSL. In the interest of security, for production this is not recommended.
|
||||
To update the configuration, use `mc admin config get` command to get the current configuration.
|
||||
@@ -985,7 +985,7 @@ dsn_string* (string) MySQL data-source-name connection string e.g.
|
||||
table* (string) DB table name to store/update events, table is auto-created
|
||||
format* (namespace*|access) 'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'
|
||||
queue_dir (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
comment (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
@@ -1000,13 +1000,13 @@ MINIO_NOTIFY_MYSQL_DSN_STRING* (string) MySQL data-source-name conn
|
||||
MINIO_NOTIFY_MYSQL_TABLE* (string) DB table name to store/update events, table is auto-created
|
||||
MINIO_NOTIFY_MYSQL_FORMAT* (namespace*|access) 'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'
|
||||
MINIO_NOTIFY_MYSQL_QUEUE_DIR (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
MINIO_NOTIFY_MYSQL_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
MINIO_NOTIFY_MYSQL_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
MINIO_NOTIFY_MYSQL_COMMENT (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
`dsn_string` is required and is of form `"<user>:<password>@tcp(<host>:<port>)/<database>"`
|
||||
|
||||
MinIO supports persistent event store. The persistent store will backup events if MySQL connection goes offline and then replays the stored events when the broken connection comes back up. The event store can be configured by setting a directory path in `queue_dir` field, and the maximum number of events, which can be stored in a `queue_dir`, in `queue_limit` field. For example, `queue_dir` can be set to `/home/events` and `queue_limit` can be set to `1000`. By default, the `queue_limit` is set to `10000`.
|
||||
MinIO supports persistent event store. The persistent store will backup events if MySQL connection goes offline and then replays the stored events when the broken connection comes back up. The event store can be configured by setting a directory path in `queue_dir` field, and the maximum number of events, which can be stored in a `queue_dir`, in `queue_limit` field. For example, `queue_dir` can be set to `/home/events` and `queue_limit` can be set to `1000`. By default, the `queue_limit` is set to `100000`.
|
||||
|
||||
Before updating the configuration, let's start with `mc admin config get` command to get the current configuration.
|
||||
|
||||
@@ -1077,7 +1077,7 @@ MinIO requires Kafka version 0.10 or 0.9. Internally MinIO uses the [Shopify/sar
|
||||
|
||||
### Step 2: Add Kafka endpoint to MinIO
|
||||
|
||||
MinIO supports persistent event store. The persistent store will backup events when the kafka broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 10000.
|
||||
MinIO supports persistent event store. The persistent store will backup events when the kafka broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 100000.
|
||||
|
||||
```
|
||||
KEY:
|
||||
@@ -1096,7 +1096,7 @@ tls_skip_verify (on|off) trust server TLS without verification, defaults to
|
||||
client_tls_cert (path) path to client certificate for mTLS auth
|
||||
client_tls_key (path) path to client key for mTLS auth
|
||||
queue_dir (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
version (string) specify the version of the Kafka cluster e.g '2.2.0'
|
||||
comment (sentence) optionally add a comment to this setting
|
||||
```
|
||||
@@ -1120,7 +1120,7 @@ MINIO_NOTIFY_KAFKA_TLS_SKIP_VERIFY (on|off) trust server TLS wit
|
||||
MINIO_NOTIFY_KAFKA_CLIENT_TLS_CERT (path) path to client certificate for mTLS auth
|
||||
MINIO_NOTIFY_KAFKA_CLIENT_TLS_KEY (path) path to client key for mTLS auth
|
||||
MINIO_NOTIFY_KAFKA_QUEUE_DIR (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
MINIO_NOTIFY_KAFKA_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
MINIO_NOTIFY_KAFKA_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
MINIO_NOTIFY_KAFKA_COMMENT (sentence) optionally add a comment to this setting
|
||||
MINIO_NOTIFY_KAFKA_VERSION (string) specify the version of the Kafka cluster e.g. '2.2.0'
|
||||
```
|
||||
@@ -1230,7 +1230,7 @@ kafkacat -b localhost:9092 -t bucketevents
|
||||
|
||||
### Step 1: Add Webhook endpoint to MinIO
|
||||
|
||||
MinIO supports persistent event store. The persistent store will backup events when the webhook goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 10000.
|
||||
MinIO supports persistent event store. The persistent store will backup events when the webhook goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 100000.
|
||||
|
||||
```
|
||||
KEY:
|
||||
@@ -1240,7 +1240,7 @@ ARGS:
|
||||
endpoint* (url) webhook server endpoint e.g. http://localhost:8080/minio/events
|
||||
auth_token (string) opaque string or JWT authorization token
|
||||
queue_dir (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
comment (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
@@ -1254,7 +1254,7 @@ MINIO_NOTIFY_WEBHOOK_ENABLE* (on|off) enable notify_webhook target, defa
|
||||
MINIO_NOTIFY_WEBHOOK_ENDPOINT* (url) webhook server endpoint e.g. http://localhost:8080/minio/events
|
||||
MINIO_NOTIFY_WEBHOOK_AUTH_TOKEN (string) opaque string or JWT authorization token
|
||||
MINIO_NOTIFY_WEBHOOK_QUEUE_DIR (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
MINIO_NOTIFY_WEBHOOK_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
MINIO_NOTIFY_WEBHOOK_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
MINIO_NOTIFY_WEBHOOK_COMMENT (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
@@ -1333,7 +1333,7 @@ docker run --rm -p 4150-4151:4150-4151 nsqio/nsq /nsqd
|
||||
|
||||
### Step 1: Add NSQ endpoint to MinIO
|
||||
|
||||
MinIO supports persistent event store. The persistent store will backup events when the NSQ broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 10000.
|
||||
MinIO supports persistent event store. The persistent store will backup events when the NSQ broker goes offline and replays it when the broker comes back online. The event store can be configured by setting the directory path in `queue_dir` field and the maximum limit of events in the queue_dir in `queue_limit` field. For eg, the `queue_dir` can be `/home/events` and `queue_limit` can be `1000`. By default, the `queue_limit` is set to 100000.
|
||||
|
||||
To update the configuration, use `mc admin config get` command to get the current configuration for `notify_nsq`.
|
||||
|
||||
@@ -1347,7 +1347,7 @@ topic* (string) NSQ topic
|
||||
tls (on|off) set to 'on' to enable TLS
|
||||
tls_skip_verify (on|off) trust server TLS without verification, defaults to "on" (verify)
|
||||
queue_dir (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
queue_limit (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
comment (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
@@ -1363,7 +1363,7 @@ MINIO_NOTIFY_NSQ_TOPIC* (string) NSQ topic
|
||||
MINIO_NOTIFY_NSQ_TLS (on|off) set to 'on' to enable TLS
|
||||
MINIO_NOTIFY_NSQ_TLS_SKIP_VERIFY (on|off) trust server TLS without verification, defaults to "on" (verify)
|
||||
MINIO_NOTIFY_NSQ_QUEUE_DIR (path) staging dir for undelivered messages e.g. '/home/events'
|
||||
MINIO_NOTIFY_NSQ_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '10000'
|
||||
MINIO_NOTIFY_NSQ_QUEUE_LIMIT (number) maximum limit for undelivered messages, defaults to '100000'
|
||||
MINIO_NOTIFY_NSQ_COMMENT (sentence) optionally add a comment to this setting
|
||||
```
|
||||
|
||||
|
||||
@@ -4,6 +4,5 @@ MinIO Gateway adds Amazon S3 compatibility to third party cloud storage provider
|
||||
- [Microsoft Azure Blob Storage](https://github.com/minio/minio/blob/master/docs/gateway/azure.md)
|
||||
- [S3](https://github.com/minio/minio/blob/master/docs/gateway/s3.md)
|
||||
- [Google Cloud Storage](https://github.com/minio/minio/blob/master/docs/gateway/gcs.md)
|
||||
- [Alibaba Cloud Storage](https://github.com/minio/minio/blob/master/docs/gateway/oss.md)
|
||||
- [Backblaze B2](https://github.com/minio/minio/blob/master/docs/gateway/b2.md)
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
# MinIO OSS Gateway [](https://slack.min.io)
|
||||
MinIO Gateway adds Amazon S3 compatibility to Alibaba Cloud Object Storage Service (OSS).
|
||||
|
||||
## Run MinIO Gateway for OSS.
|
||||
|
||||
### Using Docker
|
||||
```
|
||||
docker run -p 9000:9000 --name azure-s3 \
|
||||
-e "MINIO_ACCESS_KEY=ossaccesskey" \
|
||||
-e "MINIO_SECRET_KEY=osssecretkey" \
|
||||
minio/minio gateway oss
|
||||
```
|
||||
|
||||
### Using Binary
|
||||
```
|
||||
export MINIO_ACCESS_KEY=ossaccesskey
|
||||
export MINIO_SECRET_KEY=osssecretkey
|
||||
minio gateway oss
|
||||
```
|
||||
|
||||
## Test using MinIO Browser
|
||||
MinIO Gateway comes with an embedded web based object browser. Point your web browser to http://127.0.0.1:9000 to ensure that your server has started successfully.
|
||||
|
||||

|
||||
|
||||
## Test using MinIO Client `mc`
|
||||
`mc` provides a modern alternative to UNIX commands such as ls, cat, cp, mirror, diff etc. It supports filesystems and Amazon S3 compatible cloud storage services.
|
||||
|
||||
### Configure `mc`
|
||||
```
|
||||
mc config host add myoss http://gateway-ip:9000 ossaccesskey osssecretkey
|
||||
```
|
||||
|
||||
### List buckets on OSS
|
||||
```
|
||||
mc ls myb2
|
||||
[2017-02-22 01:50:43 PST] 0B ferenginar/
|
||||
[2017-02-26 21:43:51 PST] 0B my-bucket/
|
||||
[2017-02-26 22:10:11 PST] 0B test-bucket1/
|
||||
```
|
||||
|
||||
### Known limitations
|
||||
|
||||
Gateway inherits the following OSS limitations:
|
||||
|
||||
- Bucket names with "." in the bucket name are not supported.
|
||||
- Custom metadata with "_" in the key is not supported.
|
||||
|
||||
Other limitations:
|
||||
|
||||
- Bucket notification APIs are not supported.
|
||||
|
||||
## Explore Further
|
||||
|
||||
- [`mc` command-line interface](https://docs.min.io/docs/minio-client-quickstart-guide)
|
||||
- [`aws` command-line interface](https://docs.min.io/docs/aws-cli-with-minio)
|
||||
- [`minio-go` Go SDK](https://docs.min.io/docs/golang-client-quickstart-guide)
|
||||
@@ -0,0 +1,69 @@
|
||||
# Using MinIO with Veeam
|
||||
|
||||
When using Veeam Backup and Replication, you can use S3 compatible object storage such as MinIO as a capacity tier for backups. This disaggregates storage for the Veeam infrastructure and allows you to retain control of your data. With the ease of use of setup and administration of MinIO, it allows a Veeam backup admin to easily deploy their own object store for capacity tiering.
|
||||
|
||||
__Prerequisites__
|
||||
- One or both of Veeam Backup and Replication with support for S3 compatible object store (e.g. 9.5.4) and Veeam Backup for Office365 (VBO)
|
||||
- MinIO object storage set up per https://docs.min.io/docs/minio-quickstart-guide.html
|
||||
- Veeam requires TLS connections to the object storage. This can be configured per https://docs.minio.io/docs/how-to-secure-access-to-minio-server-with-tls.html
|
||||
- The S3 bucket, Access Key and Secret Key have to be created before and outside of Veeam.
|
||||
- Configure the minio client for the Veeam MinIO endpoint - https://docs.min.io/docs/minio-client-quickstart-guide.html
|
||||
|
||||
|
||||
|
||||
## Setting up an S3 compatible object store for Veeam Backup and Replication
|
||||
### Create a bucket for Veeam backups
|
||||
Create a bucket for Veeam Backup, e.g., `mc mb myminio/veeambackup`
|
||||
|
||||
### Add MinIO as an object store for Veeam
|
||||
Follow the steps from the Veeam documentation for adding MinIO as an object store - https://helpcenter.veeam.com/docs/backup/vsphere/adding_s3c_object_storage.html?ver=100
|
||||
|
||||
|
||||
### Creating the Scale-out Backup Repository
|
||||
|
||||
1. Under the Backup Infrastructure view, click on Scale-out Repositories and click the Add Scale-out Repository button on the ribbon.
|
||||
|
||||
2. Follow the on screen wizard
|
||||
|
||||
3. On the Capacity Tier screen, check the box to Extend scale-out backup repository capacity with object storage checkbox and select the object storage. If you want to be able to test backup data immediately after a job is run, under the object storage selection, check the "Copy" box and uncheck the "Move" box.
|
||||
|
||||
|
||||
### Create a backup job
|
||||
#### Backup Virtual Machines with Veeam Backup and Replication
|
||||
|
||||
1. Under Home > Jobs > Backup in Navigation Pane, click on Backup Job button in the ribbon and choose Virtual Machine. Follow the on screen wizard.
|
||||
|
||||
2. On the Storage screen, choose the Scale-out Backup Repository that was configured previously.
|
||||
|
||||
4. Continue with the backup job creation. On the Summary screen, check the Run the Job when I click Finish checkbox and click the Finish button.
|
||||
The backup job will start immediately. This will create an Active Full backup of the VMs within the backup job.
|
||||
|
||||
5. Since we selected Copy mode when creating the SOBR, the backup will be copied to the capacity tier as soon as it is created on the performance tier.
|
||||
|
||||
#### Backup Office 365 with VBO
|
||||
1. Create a new bucket for VBO backups
|
||||
`mc mb myminio/vbo`
|
||||
|
||||
2. Under Backup Infrastructure, right click on Object Storage Repositories and choose "Add object storage"
|
||||
|
||||

|
||||
|
||||
3. Follow through the wizard as above for Veeam Backup and Replication as the steps are the same between both products
|
||||
|
||||
4. Under Backup Infrastructure -> Backup Repositories, right click and "Add Backup Repository"
|
||||
|
||||
5. Follow the wizard. Under the "Object Storage Backup Repository" section, choose the MinIO object storage you created above
|
||||
|
||||

|
||||
|
||||
6. When you create your backup job, choose the backup repository you created above.
|
||||
|
||||
|
||||
## Test the setup
|
||||
The next time the backup job runs, you can use the `mc admin trace myminio` command and verify traffic is flowing to the MinIO nodes. For Veeam Backup and Replication you will need to wait for the backup to complete to the performance tier before it migrates data to the capacity tier (i.e., MinIO).
|
||||
|
||||
```
|
||||
20:09:10.216 [200 OK] s3.GetObject veeam-minio01:9000/vbo/Veeam/Backup365/vbotest/Organizations/6571606ecbc4455dbfe23b83f6f45597/Webs/ca2d0986229b4ec88e3a217ef8f04a1d/Items/efaa67764b304e77badb213d131beab6/f4f0cf600f494c3eb702d8eafe0fabcc.aac07493e6cd4c71845d2495a4e1e19b 139.178.68.158 9.789ms ↑ 90 B ↓ 8.5 KiB
|
||||
20:09:10.244 [200 OK] s3.GetObject veeam-minio01:9000/vbo/Veeam/Backup365/vbotest/RepositoryLock/cad99aceb50c49ecb9e07246c3b9fadc_bfd985e5deec4cebaf481847f2c34797 139.178.68.158 16.21ms ↑ 90 B ↓ 402 B
|
||||
20:09:10.283 [200 OK] s3.PutObject veeam-minio01:9000/vbo/Veeam/Backup365/vbotest/CommonInfo/WebRestorePoints/18f1aba8f55f4ac6b805c4de653eb781 139.178.68.158 29.787ms ↑ 1005 B ↓ 296 B
|
||||
```
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
+1
-1
@@ -4,7 +4,7 @@ MinIO uses a key-management-system (KMS) to support SSE-S3. If a client requests
|
||||
is enabled, the MinIO server encrypts each object with an unique object key which is protected by a master key
|
||||
managed by the KMS.
|
||||
|
||||
> MinIO still provides native Hashicorp Vault support. However, this is feature is **deprecated** and may be
|
||||
> MinIO still provides native Hashicorp Vault support. However, this feature is **deprecated** and may be
|
||||
> removed in the future. Therefore, we strongly recommend to use the architecture and KMS Guide below.
|
||||
> If you have to maintain a legacy MinIO-Vault deployment you can find the legacy documentation [here](https://docs.min.io/docs/minio-vault-legacy.html).
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ version: '3.7'
|
||||
# 9001 through 9004.
|
||||
services:
|
||||
minio1:
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
volumes:
|
||||
- data1-1:/data1
|
||||
- data1-2:/data2
|
||||
@@ -22,7 +22,7 @@ services:
|
||||
retries: 3
|
||||
|
||||
minio2:
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
volumes:
|
||||
- data2-1:/data1
|
||||
- data2-2:/data2
|
||||
@@ -39,7 +39,7 @@ services:
|
||||
retries: 3
|
||||
|
||||
minio3:
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
volumes:
|
||||
- data3-1:/data1
|
||||
- data3-2:/data2
|
||||
@@ -56,7 +56,7 @@ services:
|
||||
retries: 3
|
||||
|
||||
minio4:
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
volumes:
|
||||
- data4-1:/data1
|
||||
- data4-2:/data2
|
||||
|
||||
@@ -2,7 +2,7 @@ version: '3.7'
|
||||
|
||||
services:
|
||||
minio1:
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
hostname: minio1
|
||||
volumes:
|
||||
- minio1-data:/export
|
||||
@@ -29,7 +29,7 @@ services:
|
||||
retries: 3
|
||||
|
||||
minio2:
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
hostname: minio2
|
||||
volumes:
|
||||
- minio2-data:/export
|
||||
@@ -56,7 +56,7 @@ services:
|
||||
retries: 3
|
||||
|
||||
minio3:
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
hostname: minio3
|
||||
volumes:
|
||||
- minio3-data:/export
|
||||
@@ -83,7 +83,7 @@ services:
|
||||
retries: 3
|
||||
|
||||
minio4:
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
hostname: minio4
|
||||
volumes:
|
||||
- minio4-data:/export
|
||||
|
||||
@@ -2,7 +2,7 @@ version: '3.7'
|
||||
|
||||
services:
|
||||
minio1:
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
hostname: minio1
|
||||
volumes:
|
||||
- minio1-data:/export
|
||||
@@ -33,7 +33,7 @@ services:
|
||||
retries: 3
|
||||
|
||||
minio2:
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
hostname: minio2
|
||||
volumes:
|
||||
- minio2-data:/export
|
||||
@@ -64,7 +64,7 @@ services:
|
||||
retries: 3
|
||||
|
||||
minio3:
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
hostname: minio3
|
||||
volumes:
|
||||
- minio3-data:/export
|
||||
@@ -95,7 +95,7 @@ services:
|
||||
retries: 3
|
||||
|
||||
minio4:
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
hostname: minio4
|
||||
volumes:
|
||||
- minio4-data:/export
|
||||
|
||||
@@ -30,7 +30,7 @@ spec:
|
||||
value: "minio"
|
||||
- name: MINIO_SECRET_KEY
|
||||
value: "minio123"
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
# Unfortunately you must manually define each server. Perhaps autodiscovery via DNS can be implemented in the future.
|
||||
args:
|
||||
- server
|
||||
|
||||
@@ -22,7 +22,7 @@ spec:
|
||||
value: "minio"
|
||||
- name: MINIO_SECRET_KEY
|
||||
value: "minio123"
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
args:
|
||||
- server
|
||||
- http://minio-{0...3}.minio.default.svc.cluster.local/data
|
||||
|
||||
@@ -24,7 +24,7 @@ spec:
|
||||
containers:
|
||||
- name: minio
|
||||
# Pulls the default Minio image from Docker Hub
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
args:
|
||||
- gateway
|
||||
- gcs
|
||||
|
||||
@@ -32,7 +32,7 @@ spec:
|
||||
- name: data
|
||||
mountPath: "/data"
|
||||
# Pulls the lastest Minio image from Docker Hub
|
||||
image: minio/minio:RELEASE.2020-04-10T03-34-42Z
|
||||
image: minio/minio:RELEASE.2020-04-22T00-11-12Z
|
||||
args:
|
||||
- server
|
||||
- /data
|
||||
|
||||
@@ -11,9 +11,7 @@ require (
|
||||
github.com/Shopify/sarama v1.24.1
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
||||
github.com/alecthomas/participle v0.2.1
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5
|
||||
github.com/aws/aws-sdk-go v1.20.21
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
|
||||
github.com/bcicen/jstream v0.0.0-20190220045926-16c1f8af81c2
|
||||
github.com/beevik/ntp v0.2.0
|
||||
github.com/cespare/xxhash/v2 v2.1.1
|
||||
@@ -96,7 +94,6 @@ require (
|
||||
github.com/rcrowley/go-metrics v0.0.0-20190704165056-9c2d0518ed81 // indirect
|
||||
github.com/rjeczalik/notify v0.9.2
|
||||
github.com/rs/cors v1.6.0
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/secure-io/sio-go v0.3.0
|
||||
github.com/shirou/gopsutil v2.20.3-0.20200314133625-53cec6b37e6a+incompatible
|
||||
github.com/sirupsen/logrus v1.5.0
|
||||
|
||||
@@ -25,16 +25,12 @@ github.com/alecthomas/participle v0.2.1 h1:4AVLj1viSGa4LG5HDXKXrm5xRx19SB/rS/skP
|
||||
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=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5 h1:nWDRPCyCltiTsANwC/n3QZH7Vww33Npq9MKqlwRzI/c=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM=
|
||||
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aws/aws-sdk-go v1.20.21 h1:22vHWL9rur+SRTYPHAXlxJMFIA9OSYsYDIAHFDhQ7Z0=
|
||||
github.com/aws/aws-sdk-go v1.20.21/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA=
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
|
||||
github.com/bcicen/jstream v0.0.0-20190220045926-16c1f8af81c2 h1:M+TYzBcNIRyzPRg66ndEqUMd7oWDmhvdQmaPC6EZNwM=
|
||||
github.com/bcicen/jstream v0.0.0-20190220045926-16c1f8af81c2/go.mod h1:RDu/qcrnpEdJC/p8tx34+YBFqqX71lB7dOX9QE+ZC4M=
|
||||
github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw=
|
||||
@@ -385,8 +381,6 @@ github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/secure-io/sio-go v0.3.0 h1:QKGb6rGJeiExac9wSWxnWPYo8O8OFN7lxXQvHshX6vo=
|
||||
github.com/secure-io/sio-go v0.3.0/go.mod h1:D3KmXgKETffyYxBdFRN+Hpd2WzhzqS0EQwT3XWsAcBU=
|
||||
github.com/shirou/gopsutil v2.20.3-0.20200314133625-53cec6b37e6a+incompatible h1:YiKUe2ZOmfpDBH4OSyxwkx/mjNqHHnNhOtZ2mPyRme8=
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
FROM ubuntu:16.04
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
|
||||
ENV GOROOT /usr/local/go
|
||||
|
||||
ENV GOPATH /usr/local
|
||||
|
||||
ENV PATH $GOPATH/bin:$GOROOT/bin:$PATH
|
||||
|
||||
WORKDIR /mint
|
||||
|
||||
RUN apt-get --yes update && apt-get --yes upgrade && \
|
||||
apt-get --yes --quiet install wget jq curl dnsmasq
|
||||
|
||||
ENV MINT_ROOT_DIR /mint
|
||||
ENV MINT_RUN_CORE_DIR $MINT_ROOT_DIR/run/core
|
||||
ENV MINT_RUN_SECURITY_DIR $MINT_ROOT_DIR/run/security
|
||||
ENV WGET "wget --quiet --no-check-certificate"
|
||||
|
||||
COPY create-data-files.sh /mint
|
||||
RUN /mint/create-data-files.sh
|
||||
|
||||
COPY install-packages.list /mint
|
||||
COPY preinstall.sh /mint
|
||||
RUN /mint/preinstall.sh
|
||||
|
||||
COPY run /mint/run
|
||||
|
||||
COPY build/awscli /mint/build/awscli
|
||||
RUN build/awscli/install.sh
|
||||
|
||||
COPY build/aws-sdk-java /mint/build/aws-sdk-java
|
||||
RUN build/aws-sdk-java/install.sh
|
||||
|
||||
COPY build/aws-sdk-go /mint/build/aws-sdk-go
|
||||
RUN build/aws-sdk-go/install.sh
|
||||
|
||||
COPY build/aws-sdk-php /mint/build/aws-sdk-php
|
||||
RUN build/aws-sdk-php/install.sh
|
||||
|
||||
COPY build/aws-sdk-ruby /mint/build/aws-sdk-ruby
|
||||
RUN build/aws-sdk-ruby/install.sh
|
||||
|
||||
COPY build/mc /mint/build/mc
|
||||
RUN build/mc/install.sh
|
||||
|
||||
COPY build/minio-go /mint/build/minio-go
|
||||
RUN build/minio-go/install.sh
|
||||
|
||||
COPY build/minio-java /mint/build/minio-java
|
||||
RUN build/minio-java/install.sh
|
||||
|
||||
COPY build/minio-js /mint/build/minio-js
|
||||
RUN build/minio-js/install.sh
|
||||
|
||||
COPY build/minio-py /mint/build/minio-py
|
||||
RUN build/minio-py/install.sh
|
||||
|
||||
COPY build/s3cmd /mint/build/s3cmd
|
||||
RUN build/s3cmd/install.sh
|
||||
|
||||
COPY build/minio-dotnet/ /mint/build/minio-dotnet/
|
||||
RUN /mint/build/minio-dotnet/install.sh
|
||||
|
||||
COPY build/security /mint/build/security
|
||||
RUN build/security/install.sh
|
||||
|
||||
COPY build/worm /mint/build/worm
|
||||
RUN build/worm/install.sh
|
||||
|
||||
COPY build/healthcheck /mint/build/healthcheck
|
||||
RUN build/healthcheck/install.sh
|
||||
|
||||
COPY build/s3select /mint/build/s3select
|
||||
RUN build/s3select/install.sh
|
||||
|
||||
COPY remove-packages.list /mint
|
||||
COPY postinstall.sh /mint
|
||||
RUN /mint/postinstall.sh
|
||||
|
||||
COPY mint.sh /mint/mint.sh
|
||||
COPY entrypoint.sh /mint/entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/mint/entrypoint.sh"]
|
||||
+1
-1
@@ -82,7 +82,7 @@ All test logs are stored in `/mint/log/log.json` as multiple JSON document. Bel
|
||||
After making changes to Mint source code a local docker image can be built/run by
|
||||
|
||||
```sh
|
||||
$ docker build -t minio/mint . -f Dockerfile.dev
|
||||
$ docker build -t minio/mint . -f Dockerfile.mint
|
||||
$ docker run -e SERVER_ENDPOINT=play.minio.io:9000 -e ACCESS_KEY=Q3AM3UQ867SPQQA43P2F \
|
||||
-e SECRET_KEY=zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG \
|
||||
-e ENABLE_HTTPS=1 -e MINT_MODE=full minio/mint:latest
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash -e
|
||||
#
|
||||
# Mint (C) 2017 Minio, Inc.
|
||||
# Mint (C) 20172-2020 Minio, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -15,4 +15,4 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
gem install --no-rdoc --no-ri aws-sdk-resources:3.56.0 aws-sdk multipart_body
|
||||
gem install --no-rdoc --no-ri aws-sdk-resources aws-sdk multipart_body
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash -e
|
||||
#
|
||||
# Mint (C) 2017 Minio, Inc.
|
||||
# Mint (C) 2017-2020 Minio, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -15,6 +15,4 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
AWS_CLI_VERSION="1.16.309"
|
||||
|
||||
python -m pip install awscli==$AWS_CLI_VERSION
|
||||
pip3 install awscli --upgrade
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Mint (C) 2017 Minio, Inc.
|
||||
# Mint (C) 2017-2020 Minio, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -39,4 +39,4 @@ rm -fr "${temp_dir}"
|
||||
|
||||
cd "$MINIO_DOTNET_SDK_PATH"
|
||||
dotnet restore /p:Configuration=Mint
|
||||
dotnet publish --runtime ubuntu.16.04-x64 --output out /p:Configuration=Mint
|
||||
dotnet publish --runtime ubuntu.18.04-x64 --output out /p:Configuration=Mint
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash -e
|
||||
#
|
||||
# Mint (C) 2017 Minio, Inc.
|
||||
# Mint (C) 2017-2020 Minio, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -22,8 +22,6 @@ if [ -z "$MINIO_PY_VERSION" ]; then
|
||||
fi
|
||||
|
||||
test_run_dir="$MINT_RUN_CORE_DIR/minio-py"
|
||||
# FIX https://github.com/pypa/pip/issues/5221
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install --user faker
|
||||
python -m pip install minio=="$MINIO_PY_VERSION"
|
||||
pip3 install --user faker
|
||||
pip3 install minio=="$MINIO_PY_VERSION"
|
||||
$WGET --output-document="$test_run_dir/tests.py" "https://raw.githubusercontent.com/minio/minio-py/${MINIO_PY_VERSION}/tests/functional/tests.py"
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
git
|
||||
python3-pip
|
||||
nodejs
|
||||
ruby
|
||||
ruby-dev
|
||||
ruby-bundler
|
||||
php
|
||||
php7.0-curl
|
||||
php-xml
|
||||
default-jre
|
||||
default-jdk
|
||||
ant
|
||||
openjdk-8-jdk
|
||||
openjdk-8-jre
|
||||
dirmngr
|
||||
apt-transport-https
|
||||
dotnet-sdk-2.1
|
||||
ca-certificates-mono
|
||||
nuget
|
||||
libunwind8
|
||||
ruby
|
||||
ruby-dev
|
||||
ruby-bundler
|
||||
php
|
||||
php-curl
|
||||
php-xml
|
||||
ant
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user