Compare commits

..

50 Commits

Author SHA1 Message Date
Anis Elleuch 40852801ea fix: Better check of RPC type requests (#6927)
guessIsRPCReq() considers all POST requests as RPC but doesn't
check if this is an object operation API or not, which is actually
confusing bucket forwarder handler when it receives a new multipart
upload API which is a POST http request.

Due to this bug, users having a federated setup are not able to
upload a multipart object using an endpoint which doesn't actually
contain the specified bucket that will store the object.

Hence this commit will fix the described issue.
2018-12-05 14:28:48 -08:00
poornas f6980c4630 fix ConfigSys and NotificationSys initialization for NAS (#6920) 2018-12-05 14:03:42 -08:00
Harshavardhana 8fcc787cba Register notFound handler only once per root router (#6926)
registering notFound handler more than once causes
gorilla mux to return error for all registered paths
greater than > 8. This might be a bug in the gorilla/mux
but we shouldn't be using it this way. NotFound handler
should be only registered once per root router.

Fixes #6915
2018-12-05 11:54:12 -08:00
Praveen raj Mani 4e6d3c093f Errors in notification config should not crash the server (#6881)
Fixes #6870
2018-12-04 18:27:12 -08:00
Anis Elleuch 61145361fd Fallback to non-loopback IF addresses for Domain IPs (#6918)
When MINIO_PUBLIC_IPS is not specified and no endpoints are passed
as arguments, fallback to the address of non loop-back interfaces.

This is useful so users can avoid setting MINIO_PUBLIC_IPS in docker
or orchestration scripts, ince users naturally setup an internal
network that connects all instances.
2018-12-04 17:35:22 -08:00
James Neiman, President 950b4ad9af Update to How to secure access to Minio server with TLS (#6845) 2018-12-04 17:30:39 -08:00
Harshavardhana 6add646130 Fix logging of initialization errors in distributed mode (#6914) 2018-12-04 10:25:56 -08:00
Harshavardhana 20e61fb362 Redirect browser requests only if browser is enabled (#6909)
This PR fixes an issue introduced in PR #6848, when
browser is disabled we shouldn't re-direct the requests
returning AccessDenied.

Fixes #6907
2018-12-04 13:08:24 +05:30
Bala FA 18ced1102c handle post policy only if it is set. (#6852)
Previously policy in post form is assumed to be set always.  This is
fixed by doing the check when policy is set.
2018-12-03 12:01:28 -08:00
Harshavardhana d6af3c1237 Add bucket notification support for NAS gateway (#6908)
Fixes #6885
2018-12-03 14:02:14 +05:30
Andreas Auernhammer 5549a44566 rename vault namespace env variable to be more idiomatic (#6905)
This commit renames the env variable for vault namespaces
such that it begins with `MINIO_SSE_`. This is the prefix
for all Minio SSE related env. variables (like KMS).
2018-12-01 05:28:49 -08:00
Praveen raj Mani e7af31c2ff Removed clientID from NATS-Streaming Config (#6391)
clientID must be a unique `UUID` for each connections. Now, the
server generates it, rather considering the config.

Removing it as it is non-beneficial right now.

Fixes #6364
2018-11-30 10:46:17 +05:30
Minio Trusted e7971b1d55 Update yaml files to latest version RELEASE.2018-11-30T03-56-59Z 2018-11-30 04:02:10 +00:00
Harshavardhana 26120d7838 Ignore permission errors on config-dir (#6894) 2018-11-29 18:14:05 -08:00
Harshavardhana bef7c01c58 Choose right users in federation mode for CopyObject (#6895) 2018-11-29 17:35:11 -08:00
poornas 6a8ccc5925 update README.md (#6893) 2018-11-29 15:50:57 -08:00
kannappanr d85199e9de Vendorize minio-go (#6883)
Fixes #6873
2018-11-29 11:13:03 -08:00
Harshavardhana 8608a84c23 Ignore hidden directory .snapshot for NetApp volumes (#6889) 2018-11-29 11:39:21 +05:30
Harshavardhana b7226f4c82 Add transaction lock when migrating configs (#6878)
When migrating configs it happens often that some
servers fail to start due to version mismatch etc.

Hold a transaction lock such that all servers get
serialized.
2018-11-28 17:38:23 -08:00
Pontus Leitzler f930ffe9e2 Fix gcs context (#6869) 2018-11-28 13:49:51 +05:30
James Neiman, President b50a245208 Update to Minio Multi-Tenant Deployment Guide (#6871)
Initial edits.
2018-11-27 18:03:07 -08:00
poornas 45bb11e020 Set namespace on vault client if VAULT_NAMESPACE env is set (#6867) 2018-11-27 14:42:32 -08:00
jingsam b65cf281fd Update azure.md (#6834) 2018-11-27 14:05:27 -08:00
Xie Yanbo f781548b0c fix typo (#6812) 2018-11-27 14:04:50 -08:00
jingsam 25ee8e74f7 Update README.md (#6832) 2018-11-27 14:04:11 -08:00
jingsam c975d2cc7e Update README.md (#6833) 2018-11-27 14:03:08 -08:00
jingsam ea66528739 Update gcs.md (#6835) 2018-11-27 14:02:23 -08:00
Harshavardhana e1164103d4 Reject if tokens are missing for temp credentials (#6860) 2018-11-27 13:24:04 -08:00
Harshavardhana 83fe70f710 Fix CopyObject regression calculating md5sum (#6868)
CopyObject() failed to calculate proper md5sum
when without encryption headers. This is a regression
fix perhaps introduced in commit 5f6d717b7a

Fixes https://github.com/minio/minio-go/issues/1044
2018-11-27 13:23:32 -08:00
Harshavardhana 12a6523fb2 Do not delete parts in multipart if 0 bytes (#6855)
This can create inconsistencies i.e Parts might have
lesser number of parts than ChecksumInfos. This will
result in object to be not readable.

This PR also allows for deleting previously created
corrupted objects.
2018-11-26 13:20:21 -08:00
Harshavardhana dba61867e8 Redirect browser requests returning AccessDenied (#6848)
Anonymous requests from S3 resources returning
AccessDenied should be auto redirected to browser
for login.
2018-11-26 12:15:12 -08:00
Anis Elleuch dd092f6c2b gateway: Properly set globalMinioPort (#6859)
globalMinioPort is used in federation which stores the address
and the port number of the server hosting the specified bucket,
this latter uses globalMinioPort but this latter is not set in
startup of the gateway mode.

This commit fixes the behavior.
2018-11-26 23:19:38 +05:30
Nitish Tiwari 2a810c7da2 Improve du thread performance (#6849) 2018-11-26 10:35:14 +05:30
Nitish Tiwari dd8c2aa5c6 Cleanup Kubernetes documentation (#6861)
Also add details on why Readiness checks are not recommended for Minio
StatefulSets.
2018-11-25 13:34:20 -08:00
Harshavardhana 9e3fce441e Audit log claims from token (#6847) 2018-11-22 09:33:24 +05:30
Harshavardhana d4265f9a13 Simplify OPA to use rootCAs custom transport (#6843)
Also close the connections properly to use the
connection pooling properly for HTTP clients.
2018-11-22 08:31:05 +05:30
Minio Trusted 2fc024e880 Update yaml files to latest version RELEASE.2018-11-22T02-51-56Z 2018-11-22 02:57:28 +00:00
Harshavardhana eddf468aef Lock the targetList properly in go-routines (#6838)
Fixes #6483
2018-11-21 21:25:54 +05:30
Ashish Kumar Sinha b0d04b9a81 Retry Connection for RabbitMQ (#6837)
Add retries to connect to RabbitMQ 5 times 
with 2s interval

Fixes #6807
2018-11-21 08:37:29 +05:30
Harshavardhana a9de303d8b Update command line docs (#6839) 2018-11-20 17:35:33 -08:00
Anis Elleuch 69bd6df464 storage: Implement Close() in REST client (#6826)
Calling /minio/prometheuses/metrics calls xlSets.StorageInfo() which creates a new
storage REST client and closes it. However, currently, closing does nothing
to the underlying opened http client.

This commit introduces a closing behavior by calling CloseIdleConnections
provided by http.Transport upon the initialization of this latter.
2018-11-20 11:07:19 -08:00
Harshavardhana bfb505aa8e Refactor logging in more Go idiomatic style (#6816)
This refactor brings a change which allows
targets to be added in a cleaner way and also
audit is now moved out.

This PR also simplifies logger dependency for auditing
2018-11-19 14:47:03 -08:00
poornas d732b1ff9d Fix to cache objects on downloads (#6828)
fixes #6817
2018-11-19 11:00:46 -08:00
Igor K ef517bd0da update for build on DilOS (#6770) 2018-11-19 19:40:02 +05:30
Minio Trusted 32d837cf88 Update yaml files to latest version RELEASE.2018-11-17T01-23-48Z 2018-11-17 01:30:29 +00:00
Anis Elleuch 7b579caf68 heal: Fix heal sequences cleanup process (#6780)
The current code triggers a timeout to cleanup a heal seq from
healSeqMap, but we don't know if the user did or not launch a new
healing sequence with the same path.

Add endTime to healSequence struct and add a periodic heal-sequence
cleaner to remove heal sequences only if this latter is older than
10 minutes.
2018-11-16 14:59:51 -08:00
Harshavardhana 272b8003d6 Honor header only when requested for use (#6815) 2018-11-16 10:27:48 -08:00
Anis Elleuch 1c24c93f73 storage: Upgrade REST version after adding WriteAll API (#6819)
Rolling update doesn't work properly because Storage REST API has
a new API WriteAll() but without API version number increase.

Also be sure to return 404 for unknown http paths.
2018-11-15 12:18:58 -08:00
Harshavardhana 712abc7958 Fix anonymous downloads URL generation (#6800)
Fixes #6778
2018-11-14 18:05:10 -08:00
poornas 5f6d717b7a Fix: Preserve MD5Sum for SSE encrypted objects (#6680)
To conform with AWS S3 Spec on ETag for SSE-S3 encrypted objects,
encrypt client sent MD5Sum and store it on backend as ETag.Extend
this behavior to SSE-C encrypted objects.
2018-11-14 17:36:41 -08:00
177 changed files with 3789 additions and 2258 deletions
@@ -26,6 +26,8 @@ jest.mock("../../web", () => ({
.fn(() => true) .fn(() => true)
.mockReturnValueOnce(true) .mockReturnValueOnce(true)
.mockReturnValueOnce(false) .mockReturnValueOnce(false)
.mockReturnValueOnce(true)
.mockReturnValueOnce(true)
.mockReturnValueOnce(false), .mockReturnValueOnce(false),
ListObjects: jest.fn(({ bucketName }) => { ListObjects: jest.fn(({ bucketName }) => {
if (bucketName === "test-deny") { if (bucketName === "test-deny") {
@@ -405,7 +407,7 @@ describe("Objects actions", () => {
store.dispatch(actionsObjects.downloadObject("obj1")) store.dispatch(actionsObjects.downloadObject("obj1"))
const url = `${ const url = `${
window.location.origin window.location.origin
}${minioBrowserPrefix}/download/bk1/${encodeURI("pre1/obj1")}?token=''` }${minioBrowserPrefix}/download/bk1/${encodeURI("pre1/obj1")}?token=`
expect(setLocation).toHaveBeenCalledWith(url) expect(setLocation).toHaveBeenCalledWith(url)
}) })
+45 -45
View File
@@ -16,11 +16,7 @@
import web from "../web" import web from "../web"
import history from "../history" import history from "../history"
import { import { sortObjectsByName, sortObjectsBySize, sortObjectsByDate } from "../utils"
sortObjectsByName,
sortObjectsBySize,
sortObjectsByDate
} from "../utils"
import { getCurrentBucket } from "../buckets/selectors" import { getCurrentBucket } from "../buckets/selectors"
import { getCurrentPrefix, getCheckedList } from "./selectors" import { getCurrentPrefix, getCheckedList } from "./selectors"
import * as alertActions from "../alert/actions" import * as alertActions from "../alert/actions"
@@ -60,10 +56,7 @@ export const appendList = (objects, marker, isTruncated) => ({
export const fetchObjects = append => { export const fetchObjects = append => {
return function(dispatch, getState) { return function(dispatch, getState) {
const { const {buckets: {currentBucket}, objects: {currentPrefix, marker}} = getState()
buckets: { currentBucket },
objects: { currentPrefix, marker }
} = getState()
if (currentBucket) { if (currentBucket) {
return web return web
.ListObjects({ .ListObjects({
@@ -110,7 +103,7 @@ export const fetchObjects = append => {
export const sortObjects = sortBy => { export const sortObjects = sortBy => {
return function(dispatch, getState) { return function(dispatch, getState) {
const { objects } = getState() const {objects} = getState()
const sortOrder = objects.sortBy == sortBy ? !objects.sortOrder : true const sortOrder = objects.sortBy == sortBy ? !objects.sortOrder : true
dispatch(setSortBy(sortBy)) dispatch(setSortBy(sortBy))
dispatch(setSortOrder(sortOrder)) dispatch(setSortOrder(sortOrder))
@@ -210,30 +203,39 @@ export const shareObject = (object, days, hours, minutes) => {
const currentPrefix = getCurrentPrefix(getState()) const currentPrefix = getCurrentPrefix(getState())
const objectName = `${currentPrefix}${object}` const objectName = `${currentPrefix}${object}`
const expiry = days * 24 * 60 * 60 + hours * 60 * 60 + minutes * 60 const expiry = days * 24 * 60 * 60 + hours * 60 * 60 + minutes * 60
return web if (web.LoggedIn()) {
.PresignedGet({ return web
host: location.host, .PresignedGet({
bucket: currentBucket, host: location.host,
object: objectName, bucket: currentBucket,
expiry object: objectName
}) })
.then(obj => { .then(obj => {
dispatch(showShareObject(object, obj.url)) dispatch(showShareObject(object, obj.url))
dispatch( dispatch(
alertActions.set({ alertActions.set({
type: "success", type: "success",
message: `Object shared. Expires in ${days} days ${hours} hours ${minutes} minutes` message: `Object shared. Expires in ${days} days ${hours} hours ${minutes} minutes`
}) })
) )
}) })
.catch(err => { .catch(err => {
dispatch( dispatch(
alertActions.set({ alertActions.set({
type: "danger", type: "danger",
message: err.message message: err.message
}) })
) )
}) })
} else {
dispatch(showShareObject(object, `${location.host}` + '/' + `${currentBucket}` + '/' + encodeURI(objectName)))
dispatch(
alertActions.set({
type: "success",
message: `Object shared.`
})
)
}
} }
} }
@@ -279,7 +281,7 @@ export const downloadObject = object => {
} else { } else {
const url = `${ const url = `${
window.location.origin window.location.origin
}${minioBrowserPrefix}/download/${currentBucket}/${encObjectName}?token=''` }${minioBrowserPrefix}/download/${currentBucket}/${encObjectName}?token=`
window.location = url window.location = url
} }
} }
@@ -308,7 +310,7 @@ export const downloadCheckedObjects = () => {
objects: getCheckedList(state) objects: getCheckedList(state)
} }
if (!web.LoggedIn()) { if (!web.LoggedIn()) {
const requestUrl = location.origin + "/minio/zip?token=''" const requestUrl = location.origin + "/minio/zip?token="
downloadZip(requestUrl, req, dispatch) downloadZip(requestUrl, req, dispatch)
} else { } else {
return web return web
@@ -319,14 +321,13 @@ export const downloadCheckedObjects = () => {
}${minioBrowserPrefix}/zip?token=${res.token}` }${minioBrowserPrefix}/zip?token=${res.token}`
downloadZip(requestUrl, req, dispatch) downloadZip(requestUrl, req, dispatch)
}) })
.catch(err => .catch(err => dispatch(
dispatch( alertActions.set({
alertActions.set({ type: "danger",
type: "danger", message: err.message
message: err.message })
})
)
) )
)
} }
} }
} }
@@ -349,8 +350,7 @@ const downloadZip = (url, req, dispatch) => {
var separator = req.prefix.length > 1 ? "-" : "" var separator = req.prefix.length > 1 ? "-" : ""
anchor.href = blobUrl anchor.href = blobUrl
anchor.download = anchor.download = req.bucketName + separator + req.prefix.slice(0, -1) + ".zip"
req.bucketName + separator + req.prefix.slice(0, -1) + ".zip"
anchor.click() anchor.click()
window.URL.revokeObjectURL(blobUrl) window.URL.revokeObjectURL(blobUrl)
+22 -33
View File
File diff suppressed because one or more lines are too long
+10 -10
View File
@@ -57,28 +57,28 @@ type accessControlPolicy struct {
func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "GetBucketACL") ctx := newContext(r, w, "GetBucketACL")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "GetBucketACL", mustGetClaimsFromToken(r))
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
// Allow getBucketACL if policy action is set, since this is a dummy call // Allow getBucketACL if policy action is set, since this is a dummy call
// we are simply re-purposing the bucketPolicyAction. // we are simply re-purposing the bucketPolicyAction.
if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketPolicyAction, bucket, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketPolicyAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
// Before proceeding validate if bucket exists. // Before proceeding validate if bucket exists.
_, err := objAPI.GetBucketInfo(ctx, bucket) _, err := objAPI.GetBucketInfo(ctx, bucket)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -92,7 +92,7 @@ func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.
Permission: "FULL_CONTROL", Permission: "FULL_CONTROL",
}) })
if err := xml.NewEncoder(w).Encode(acl); err != nil { if err := xml.NewEncoder(w).Encode(acl); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -106,7 +106,7 @@ func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.
func (api objectAPIHandlers) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "GetObjectACL") ctx := newContext(r, w, "GetObjectACL")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "GetObjectACL", mustGetClaimsFromToken(r))
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@@ -114,21 +114,21 @@ func (api objectAPIHandlers) GetObjectACLHandler(w http.ResponseWriter, r *http.
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
// Allow getObjectACL if policy action is set, since this is a dummy call // Allow getObjectACL if policy action is set, since this is a dummy call
// we are simply re-purposing the bucketPolicyAction. // we are simply re-purposing the bucketPolicyAction.
if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketPolicyAction, bucket, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketPolicyAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
// Before proceeding validate if object exists. // Before proceeding validate if object exists.
_, err := objAPI.GetObjectInfo(ctx, bucket, object, ObjectOptions{}) _, err := objAPI.GetObjectInfo(ctx, bucket, object, ObjectOptions{})
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -142,7 +142,7 @@ func (api objectAPIHandlers) GetObjectACLHandler(w http.ResponseWriter, r *http.
Permission: "FULL_CONTROL", Permission: "FULL_CONTROL",
}) })
if err := xml.NewEncoder(w).Encode(acl); err != nil { if err := xml.NewEncoder(w).Encode(acl); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
+3 -3
View File
@@ -578,7 +578,7 @@ func (a adminAPIHandlers) HealHandler(w http.ResponseWriter, r *http.Request) {
StartTime: nh.startTime, StartTime: nh.startTime,
}) })
if err != nil { if err != nil {
writeErrorResponse(w, toAdminAPIErrCode(ctx, err), r.URL) writeErrorResponseJSON(w, toAdminAPIErrCode(ctx, err), r.URL)
return return
} }
// Client token not specified but a heal sequence exists on a path, // Client token not specified but a heal sequence exists on a path,
@@ -859,7 +859,7 @@ func (a adminAPIHandlers) SetUserStatus(w http.ResponseWriter, r *http.Request)
// Custom IAM policies not allowed for admin user. // Custom IAM policies not allowed for admin user.
if accessKey == globalServerConfig.GetCredential().AccessKey { if accessKey == globalServerConfig.GetCredential().AccessKey {
writeErrorResponse(w, ErrInvalidRequest, r.URL) writeErrorResponseJSON(w, ErrInvalidRequest, r.URL)
return return
} }
@@ -898,7 +898,7 @@ func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) {
// Custom IAM policies not allowed for admin user. // Custom IAM policies not allowed for admin user.
if accessKey == globalServerConfig.GetCredential().AccessKey { if accessKey == globalServerConfig.GetCredential().AccessKey {
writeErrorResponse(w, ErrInvalidRequest, r.URL) writeErrorResponseJSON(w, ErrInvalidRequest, r.URL)
return return
} }
+3 -4
View File
@@ -38,7 +38,7 @@ import (
var ( var (
configJSON = []byte(`{ configJSON = []byte(`{
"version": "32", "version": "33",
"credential": { "credential": {
"accessKey": "minio", "accessKey": "minio",
"secretKey": "minio123" "secretKey": "minio123"
@@ -152,7 +152,6 @@ var (
"streaming": { "streaming": {
"enable": false, "enable": false,
"clusterID": "", "clusterID": "",
"clientID": "",
"async": false, "async": false,
"maxPubAcksInflight": 0 "maxPubAcksInflight": 0
} }
@@ -309,7 +308,7 @@ func (atb *adminXLTestBed) GenerateHealTestData(t *testing.T) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
objectName := fmt.Sprintf("%s-%d", objName, i) objectName := fmt.Sprintf("%s-%d", objName, i)
_, err = atb.objLayer.PutObject(context.Background(), bucketName, objectName, _, err = atb.objLayer.PutObject(context.Background(), bucketName, objectName,
mustGetHashReader(t, bytes.NewReader([]byte("hello")), mustGetPutObjReader(t, bytes.NewReader([]byte("hello")),
int64(len("hello")), "", ""), nil, ObjectOptions{}) int64(len("hello")), "", ""), nil, ObjectOptions{})
if err != nil { if err != nil {
t.Fatalf("Failed to create %s - %v", objectName, t.Fatalf("Failed to create %s - %v", objectName,
@@ -328,7 +327,7 @@ func (atb *adminXLTestBed) GenerateHealTestData(t *testing.T) {
} }
_, err = atb.objLayer.PutObjectPart(context.Background(), bucketName, objName, _, err = atb.objLayer.PutObjectPart(context.Background(), bucketName, objName,
uploadID, 3, mustGetHashReader(t, bytes.NewReader( uploadID, 3, mustGetPutObjReader(t, bytes.NewReader(
[]byte("hello")), int64(len("hello")), "", ""), ObjectOptions{}) []byte("hello")), int64(len("hello")), "", ""), ObjectOptions{})
if err != nil { if err != nil {
t.Fatalf("mp put error: %v", err) t.Fatalf("mp put error: %v", err)
+31 -35
View File
@@ -113,6 +113,32 @@ func initAllHealState(isErasureMode bool) {
globalAllHealState = allHealState{ globalAllHealState = allHealState{
healSeqMap: make(map[string]*healSequence), healSeqMap: make(map[string]*healSequence),
} }
go globalAllHealState.periodicHealSeqsClean()
}
func (ahs *allHealState) periodicHealSeqsClean() {
// Launch clean-up routine to remove this heal sequence (after
// it ends) from the global state after timeout has elapsed.
ticker := time.NewTicker(time.Minute * 5)
defer ticker.Stop()
for {
select {
case <-ticker.C:
now := UTCNow()
ahs.Lock()
for path, h := range ahs.healSeqMap {
if h.hasEnded() && h.endTime.Add(keepHealSeqStateDuration).Before(now) {
delete(ahs.healSeqMap, path)
}
}
ahs.Unlock()
case <-globalServiceDoneCh:
// server could be restarting - need
// to exit immediately
return
}
}
} }
// getHealSequence - Retrieve a heal sequence by path. The second // getHealSequence - Retrieve a heal sequence by path. The second
@@ -213,41 +239,6 @@ func (ahs *allHealState) LaunchNewHealSequence(h *healSequence) (
// Launch top-level background heal go-routine // Launch top-level background heal go-routine
go h.healSequenceStart() go h.healSequenceStart()
// Launch clean-up routine to remove this heal sequence (after
// it ends) from the global state after timeout has elapsed.
go func() {
var keepStateTimeout <-chan time.Time
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
everyMinute := ticker.C
for {
select {
// Check every minute if heal sequence has ended.
case <-everyMinute:
if h.hasEnded() {
keepStateTimeout = time.After(keepHealSeqStateDuration)
everyMinute = nil
}
// This case does not fire until the heal
// sequence completes.
case <-keepStateTimeout:
// Heal sequence has ended, keep
// results state duration has elapsed,
// so purge state.
ahs.Lock()
defer ahs.Unlock()
delete(ahs.healSeqMap, h.path)
return
case <-globalServiceDoneCh:
// server could be restarting - need
// to exit immediately
return
}
}
}()
b, err := json.Marshal(madmin.HealStartSuccess{ b, err := json.Marshal(madmin.HealStartSuccess{
ClientToken: h.clientToken, ClientToken: h.clientToken,
ClientAddress: h.clientAddress, ClientAddress: h.clientAddress,
@@ -321,6 +312,9 @@ type healSequence struct {
// time at which heal sequence was started // time at which heal sequence was started
startTime time.Time startTime time.Time
// time at which heal sequence has ended
endTime time.Time
// Heal client info // Heal client info
clientToken, clientAddress string clientToken, clientAddress string
@@ -498,6 +492,7 @@ func (h *healSequence) healSequenceStart() {
select { select {
case err, ok := <-h.traverseAndHealDoneCh: case err, ok := <-h.traverseAndHealDoneCh:
h.endTime = UTCNow()
h.currentStatus.updateLock.Lock() h.currentStatus.updateLock.Lock()
defer h.currentStatus.updateLock.Unlock() defer h.currentStatus.updateLock.Unlock()
// Heal traversal is complete. // Heal traversal is complete.
@@ -511,6 +506,7 @@ func (h *healSequence) healSequenceStart() {
} }
case <-h.stopSignalCh: case <-h.stopSignalCh:
h.endTime = UTCNow()
h.currentStatus.updateLock.Lock() h.currentStatus.updateLock.Lock()
h.currentStatus.Summary = healStoppedStatus h.currentStatus.Summary = healStoppedStatus
h.currentStatus.FailureDetail = errHealStopSignalled.Error() h.currentStatus.FailureDetail = errHealStopSignalled.Error()
+2
View File
@@ -23,6 +23,8 @@ import (
const ( const (
// Response request id. // Response request id.
responseRequestIDKey = "x-amz-request-id" responseRequestIDKey = "x-amz-request-id"
// Deployment id.
responseDeploymentIDKey = "x-minio-deployment-id"
) )
// ObjectIdentifier carries key name for the object to delete. // ObjectIdentifier carries key name for the object to delete.
+9
View File
@@ -297,6 +297,8 @@ const (
ErrEvaluatorInvalidTimestampFormatPatternSymbol ErrEvaluatorInvalidTimestampFormatPatternSymbol
ErrEvaluatorBindingDoesNotExist ErrEvaluatorBindingDoesNotExist
ErrMissingHeaders ErrMissingHeaders
ErrInvalidColumnIndex
ErrAdminConfigNotificationTargetsFailed ErrAdminConfigNotificationTargetsFailed
ErrAdminProfilerNotEnabled ErrAdminProfilerNotEnabled
ErrInvalidDecompressedSize ErrInvalidDecompressedSize
@@ -1431,6 +1433,11 @@ var errorCodeResponse = map[APIErrorCode]APIError{
Description: "Some headers in the query are missing from the file. Check the file and try again.", Description: "Some headers in the query are missing from the file. Check the file and try again.",
HTTPStatusCode: http.StatusBadRequest, HTTPStatusCode: http.StatusBadRequest,
}, },
ErrInvalidColumnIndex: {
Code: "InvalidColumnIndex",
Description: "The column index is invalid. Please check the service documentation and try again.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrInvalidDecompressedSize: { ErrInvalidDecompressedSize: {
Code: "XMinioInvalidDecompressedSize", Code: "XMinioInvalidDecompressedSize",
Description: "The data provided is unfit for decompression", Description: "The data provided is unfit for decompression",
@@ -1658,6 +1665,8 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) {
apiErr = ErrMissingHeaders apiErr = ErrMissingHeaders
case format.ErrParseInvalidPathComponent: case format.ErrParseInvalidPathComponent:
apiErr = ErrMissingHeaders apiErr = ErrMissingHeaders
case format.ErrInvalidColumnIndex:
apiErr = ErrInvalidColumnIndex
} }
// Compression errors // Compression errors
+10 -1
View File
@@ -568,13 +568,22 @@ func writeSuccessResponseHeadersOnly(w http.ResponseWriter) {
} }
// writeErrorRespone writes error headers // writeErrorRespone writes error headers
func writeErrorResponse(w http.ResponseWriter, errorCode APIErrorCode, reqURL *url.URL) { func writeErrorResponse(w http.ResponseWriter, errorCode APIErrorCode, reqURL *url.URL, browser bool) {
switch errorCode { switch errorCode {
case ErrSlowDown, ErrServerNotInitialized, ErrReadQuorum, ErrWriteQuorum: case ErrSlowDown, ErrServerNotInitialized, ErrReadQuorum, ErrWriteQuorum:
// Set retry-after header to indicate user-agents to retry request after 120secs. // Set retry-after header to indicate user-agents to retry request after 120secs.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
w.Header().Set("Retry-After", "120") w.Header().Set("Retry-After", "120")
case ErrAccessDenied:
// The request is from browser and also if browser
// is enabled we need to redirect.
if browser && globalIsBrowserEnabled {
w.Header().Set("Location", minioReservedBucketPath+reqURL.Path)
w.WriteHeader(http.StatusTemporaryRedirect)
return
}
} }
apiError := getAPIError(errorCode) apiError := getAPIError(errorCode)
// Generate error response. // Generate error response.
errorResponse := getAPIErrorResponse(apiError, reqURL.Path, w.Header().Get(responseRequestIDKey)) errorResponse := getAPIErrorResponse(apiError, reqURL.Path, w.Header().Get(responseRequestIDKey))
+58 -41
View File
@@ -19,10 +19,10 @@ package cmd
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/subtle"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@@ -155,49 +155,66 @@ func getSessionToken(r *http.Request) (token string) {
return r.URL.Query().Get("X-Amz-Security-Token") return r.URL.Query().Get("X-Amz-Security-Token")
} }
// Fetch claims in the security token returned by the client and validate the token. // Fetch claims in the security token returned by the client, doesn't return
func getClaimsFromToken(r *http.Request, cred auth.Credentials) (map[string]interface{}, APIErrorCode) { // errors - upon errors the returned claims map will be empty.
stsTokenCallback := func(jwtToken *jwtgo.Token) (interface{}, error) { func mustGetClaimsFromToken(r *http.Request) map[string]interface{} {
if _, ok := jwtToken.Method.(*jwtgo.SigningMethodHMAC); !ok { claims, _ := getClaimsFromToken(r)
return nil, fmt.Errorf("Unexpected signing method: %v", jwtToken.Header["alg"]) return claims
} }
if err := jwtToken.Claims.Valid(); err != nil {
return nil, errAuthentication // Fetch claims in the security token returned by the client.
} func getClaimsFromToken(r *http.Request) (map[string]interface{}, error) {
if claims, ok := jwtToken.Claims.(jwtgo.MapClaims); ok {
if _, ok = claims["accessKey"].(string); !ok {
return nil, errInvalidAccessKeyID
}
// JWT token for x-amz-security-token is signed with admin
// secret key, temporary credentials become invalid if
// server admin credentials change. This is done to ensure
// that clients cannot decode the token using the temp
// secret keys and generate an entirely new claim by essentially
// hijacking the policies. We need to make sure that this is
// based an admin credential such that token cannot be decoded
// on the client side and is treated like an opaque value.
return []byte(globalServerConfig.GetCredential().SecretKey), nil
}
return nil, errAuthentication
}
claims := make(map[string]interface{}) claims := make(map[string]interface{})
token := getSessionToken(r) token := getSessionToken(r)
if token == "" { if token == "" {
return nil, ErrNone return claims, nil
} }
stsTokenCallback := func(jwtToken *jwtgo.Token) (interface{}, error) {
// JWT token for x-amz-security-token is signed with admin
// secret key, temporary credentials become invalid if
// server admin credentials change. This is done to ensure
// that clients cannot decode the token using the temp
// secret keys and generate an entirely new claim by essentially
// hijacking the policies. We need to make sure that this is
// based an admin credential such that token cannot be decoded
// on the client side and is treated like an opaque value.
return []byte(globalServerConfig.GetCredential().SecretKey), nil
}
p := &jwtgo.Parser{
ValidMethods: []string{
jwtgo.SigningMethodHS256.Alg(),
jwtgo.SigningMethodHS512.Alg(),
},
}
jtoken, err := p.ParseWithClaims(token, jwtgo.MapClaims(claims), stsTokenCallback)
if err != nil {
return nil, err
}
if !jtoken.Valid {
return nil, errAuthentication
}
v, ok := claims["accessKey"]
if !ok {
return nil, errInvalidAccessKeyID
}
if _, ok = v.(string); !ok {
return nil, errInvalidAccessKeyID
}
return claims, nil
}
// Fetch claims in the security token returned by the client and validate the token.
func checkClaimsFromToken(r *http.Request, cred auth.Credentials) (map[string]interface{}, APIErrorCode) {
token := getSessionToken(r)
if token != "" && cred.AccessKey == "" { if token != "" && cred.AccessKey == "" {
return nil, ErrNoAccessKey return nil, ErrNoAccessKey
} }
if token != cred.SessionToken { if subtle.ConstantTimeCompare([]byte(token), []byte(cred.SessionToken)) != 1 {
return nil, ErrInvalidToken return nil, ErrInvalidToken
} }
p := &jwtgo.Parser{} claims, err := getClaimsFromToken(r)
jtoken, err := p.ParseWithClaims(token, jwtgo.MapClaims(claims), stsTokenCallback)
if err != nil { if err != nil {
return nil, toAPIErrorCode(context.Background(), errAuthentication) return nil, toAPIErrorCode(context.Background(), err)
}
if !jtoken.Valid {
return nil, toAPIErrorCode(context.Background(), errAuthentication)
} }
return claims, ErrNone return claims, ErrNone
} }
@@ -233,6 +250,11 @@ func checkRequestAuthType(ctx context.Context, r *http.Request, action policy.Ac
return s3Err return s3Err
} }
claims, s3Err := checkClaimsFromToken(r, cred)
if s3Err != ErrNone {
return s3Err
}
// LocationConstraint is valid only for CreateBucketAction. // LocationConstraint is valid only for CreateBucketAction.
var locationConstraint string var locationConstraint string
if action == policy.CreateBucketAction { if action == policy.CreateBucketAction {
@@ -256,11 +278,6 @@ func checkRequestAuthType(ctx context.Context, r *http.Request, action policy.Ac
r.Body = ioutil.NopCloser(bytes.NewReader(payload)) r.Body = ioutil.NopCloser(bytes.NewReader(payload))
} }
claims, s3Err := getClaimsFromToken(r, cred)
if s3Err != ErrNone {
return s3Err
}
if cred.AccessKey == "" { if cred.AccessKey == "" {
if globalPolicySys.IsAllowed(policy.Args{ if globalPolicySys.IsAllowed(policy.Args{
AccountName: cred.AccessKey, AccountName: cred.AccessKey,
@@ -399,7 +416,7 @@ func (a authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.handler.ServeHTTP(w, r) a.handler.ServeHTTP(w, r)
return return
} }
writeErrorResponse(w, ErrSignatureVersionNotSupported, r.URL) writeErrorResponse(w, ErrSignatureVersionNotSupported, r.URL, guessIsBrowserReq(r))
} }
// isPutAllowed - check if PUT operation is allowed on the resource, this // isPutAllowed - check if PUT operation is allowed on the resource, this
@@ -421,7 +438,7 @@ func isPutAllowed(atype authType, bucketName, objectName string, r *http.Request
return s3Err return s3Err
} }
claims, s3Err := getClaimsFromToken(r, cred) claims, s3Err := checkClaimsFromToken(r, cred)
if s3Err != ErrNone { if s3Err != ErrNone {
return s3Err return s3Err
} }
+5 -5
View File
@@ -61,7 +61,7 @@ func runPutObjectBenchmark(b *testing.B, obj ObjectLayer, objSize int) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
// insert the object. // insert the object.
objInfo, err := obj.PutObject(context.Background(), bucket, "object"+strconv.Itoa(i), objInfo, err := obj.PutObject(context.Background(), bucket, "object"+strconv.Itoa(i),
mustGetHashReader(b, bytes.NewBuffer(textData), int64(len(textData)), md5hex, sha256hex), metadata, ObjectOptions{}) mustGetPutObjReader(b, bytes.NewBuffer(textData), int64(len(textData)), md5hex, sha256hex), metadata, ObjectOptions{})
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
@@ -123,7 +123,7 @@ func runPutObjectPartBenchmark(b *testing.B, obj ObjectLayer, partSize int) {
md5hex = getMD5Hash([]byte(textPartData)) md5hex = getMD5Hash([]byte(textPartData))
var partInfo PartInfo var partInfo PartInfo
partInfo, err = obj.PutObjectPart(context.Background(), bucket, object, uploadID, j, partInfo, err = obj.PutObjectPart(context.Background(), bucket, object, uploadID, j,
mustGetHashReader(b, bytes.NewBuffer(textPartData), int64(len(textPartData)), md5hex, sha256hex), opts) mustGetPutObjReader(b, bytes.NewBuffer(textPartData), int64(len(textPartData)), md5hex, sha256hex), opts)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
@@ -204,7 +204,7 @@ func runGetObjectBenchmark(b *testing.B, obj ObjectLayer, objSize int) {
// insert the object. // insert the object.
var objInfo ObjectInfo var objInfo ObjectInfo
objInfo, err = obj.PutObject(context.Background(), bucket, "object"+strconv.Itoa(i), objInfo, err = obj.PutObject(context.Background(), bucket, "object"+strconv.Itoa(i),
mustGetHashReader(b, bytes.NewBuffer(textData), int64(len(textData)), md5hex, sha256hex), metadata, ObjectOptions{}) mustGetPutObjReader(b, bytes.NewBuffer(textData), int64(len(textData)), md5hex, sha256hex), metadata, ObjectOptions{})
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
@@ -304,7 +304,7 @@ func runPutObjectBenchmarkParallel(b *testing.B, obj ObjectLayer, objSize int) {
for pb.Next() { for pb.Next() {
// insert the object. // insert the object.
objInfo, err := obj.PutObject(context.Background(), bucket, "object"+strconv.Itoa(i), objInfo, err := obj.PutObject(context.Background(), bucket, "object"+strconv.Itoa(i),
mustGetHashReader(b, bytes.NewBuffer(textData), int64(len(textData)), md5hex, sha256hex), metadata, ObjectOptions{}) mustGetPutObjReader(b, bytes.NewBuffer(textData), int64(len(textData)), md5hex, sha256hex), metadata, ObjectOptions{})
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
@@ -344,7 +344,7 @@ func runGetObjectBenchmarkParallel(b *testing.B, obj ObjectLayer, objSize int) {
// insert the object. // insert the object.
var objInfo ObjectInfo var objInfo ObjectInfo
objInfo, err = obj.PutObject(context.Background(), bucket, "object"+strconv.Itoa(i), objInfo, err = obj.PutObject(context.Background(), bucket, "object"+strconv.Itoa(i),
mustGetHashReader(b, bytes.NewBuffer(textData), int64(len(textData)), md5hex, sha256hex), metadata, ObjectOptions{}) mustGetPutObjReader(b, bytes.NewBuffer(textData), int64(len(textData)), md5hex, sha256hex), metadata, ObjectOptions{})
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
+19 -17
View File
@@ -59,19 +59,19 @@ func validateListObjectsArgs(prefix, marker, delimiter string, maxKeys int) APIE
func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "ListObjectsV2") ctx := newContext(r, w, "ListObjectsV2")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "ListObjectsV2", mustGetClaimsFromToken(r))
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
if s3Error := checkRequestAuthType(ctx, r, policy.ListBucketAction, bucket, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.ListBucketAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -81,14 +81,14 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
prefix, token, startAfter, delimiter, fetchOwner, maxKeys, _, errCode := getListObjectsV2Args(urlValues) prefix, token, startAfter, delimiter, fetchOwner, maxKeys, _, errCode := getListObjectsV2Args(urlValues)
if errCode != ErrNone { if errCode != ErrNone {
writeErrorResponse(w, errCode, r.URL) writeErrorResponse(w, errCode, r.URL, guessIsBrowserReq(r))
return return
} }
// Validate the query params before beginning to serve the request. // Validate the query params before beginning to serve the request.
// fetch-owner is not validated since it is a boolean // fetch-owner is not validated since it is a boolean
if s3Error := validateListObjectsArgs(prefix, token, delimiter, maxKeys); s3Error != ErrNone { if s3Error := validateListObjectsArgs(prefix, token, delimiter, maxKeys); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
listObjectsV2 := objectAPI.ListObjectsV2 listObjectsV2 := objectAPI.ListObjectsV2
@@ -100,7 +100,7 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
// marshaled into S3 compatible XML header. // marshaled into S3 compatible XML header.
listObjectsV2Info, err := listObjectsV2(ctx, bucket, prefix, token, delimiter, maxKeys, fetchOwner, startAfter) listObjectsV2Info, err := listObjectsV2(ctx, bucket, prefix, token, delimiter, maxKeys, fetchOwner, startAfter)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -110,15 +110,16 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
// Read the decompressed size from the meta.json. // Read the decompressed size from the meta.json.
actualSize = listObjectsV2Info.Objects[i].GetActualSize() actualSize = listObjectsV2Info.Objects[i].GetActualSize()
if actualSize < 0 { if actualSize < 0 {
writeErrorResponse(w, ErrInvalidDecompressedSize, r.URL) writeErrorResponse(w, ErrInvalidDecompressedSize, r.URL, guessIsBrowserReq(r))
return return
} }
// Set the info.Size to the actualSize. // Set the info.Size to the actualSize.
listObjectsV2Info.Objects[i].Size = actualSize listObjectsV2Info.Objects[i].Size = actualSize
} else if crypto.IsEncrypted(listObjectsV2Info.Objects[i].UserDefined) { } else if crypto.IsEncrypted(listObjectsV2Info.Objects[i].UserDefined) {
listObjectsV2Info.Objects[i].ETag = getDecryptedETag(r.Header, listObjectsV2Info.Objects[i], false)
listObjectsV2Info.Objects[i].Size, err = listObjectsV2Info.Objects[i].DecryptedSize() listObjectsV2Info.Objects[i].Size, err = listObjectsV2Info.Objects[i].DecryptedSize()
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
} }
@@ -140,37 +141,37 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "ListObjectsV1") ctx := newContext(r, w, "ListObjectsV1")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "ListObjectsV1", mustGetClaimsFromToken(r))
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
if s3Error := checkRequestAuthType(ctx, r, policy.ListBucketAction, bucket, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.ListBucketAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
// Extract all the litsObjectsV1 query params to their native values. // Extract all the litsObjectsV1 query params to their native values.
prefix, marker, delimiter, maxKeys, _, s3Error := getListObjectsV1Args(r.URL.Query()) prefix, marker, delimiter, maxKeys, _, s3Error := getListObjectsV1Args(r.URL.Query())
if s3Error != ErrNone { if s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
// Validate the maxKeys lowerbound. When maxKeys > 1000, S3 returns 1000 but // Validate the maxKeys lowerbound. When maxKeys > 1000, S3 returns 1000 but
// does not throw an error. // does not throw an error.
if maxKeys < 0 { if maxKeys < 0 {
writeErrorResponse(w, ErrInvalidMaxKeys, r.URL) writeErrorResponse(w, ErrInvalidMaxKeys, r.URL, guessIsBrowserReq(r))
return return
} // Validate all the query params before beginning to serve the request. } // Validate all the query params before beginning to serve the request.
if s3Error := validateListObjectsArgs(prefix, marker, delimiter, maxKeys); s3Error != ErrNone { if s3Error := validateListObjectsArgs(prefix, marker, delimiter, maxKeys); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
listObjects := objectAPI.ListObjects listObjects := objectAPI.ListObjects
@@ -182,7 +183,7 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http
// marshaled into S3 compatible XML header. // marshaled into S3 compatible XML header.
listObjectsInfo, err := listObjects(ctx, bucket, prefix, marker, delimiter, maxKeys) listObjectsInfo, err := listObjects(ctx, bucket, prefix, marker, delimiter, maxKeys)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -192,15 +193,16 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http
// Read the decompressed size from the meta.json. // Read the decompressed size from the meta.json.
actualSize = listObjectsInfo.Objects[i].GetActualSize() actualSize = listObjectsInfo.Objects[i].GetActualSize()
if actualSize < 0 { if actualSize < 0 {
writeErrorResponse(w, ErrInvalidDecompressedSize, r.URL) writeErrorResponse(w, ErrInvalidDecompressedSize, r.URL, guessIsBrowserReq(r))
return return
} }
// Set the info.Size to the actualSize. // Set the info.Size to the actualSize.
listObjectsInfo.Objects[i].Size = actualSize listObjectsInfo.Objects[i].Size = actualSize
} else if crypto.IsEncrypted(listObjectsInfo.Objects[i].UserDefined) { } else if crypto.IsEncrypted(listObjectsInfo.Objects[i].UserDefined) {
listObjectsInfo.Objects[i].ETag = getDecryptedETag(r.Header, listObjectsInfo.Objects[i], false)
listObjectsInfo.Objects[i].Size, err = listObjectsInfo.Objects[i].DecryptedSize() listObjectsInfo.Objects[i].Size, err = listObjectsInfo.Objects[i].DecryptedSize()
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
} }
+87 -81
View File
@@ -89,19 +89,19 @@ func initFederatorBackend(objLayer ObjectLayer) {
func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "GetBucketLocation") ctx := newContext(r, w, "GetBucketLocation")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "GetBucketLocation", mustGetClaimsFromToken(r))
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketLocationAction, bucket, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketLocationAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -110,7 +110,7 @@ func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *
getBucketInfo = api.CacheAPI().GetBucketInfo getBucketInfo = api.CacheAPI().GetBucketInfo
} }
if _, err := getBucketInfo(ctx, bucket); err != nil { if _, err := getBucketInfo(ctx, bucket); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -139,42 +139,42 @@ func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *
func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "ListMultipartUploads") ctx := newContext(r, w, "ListMultipartUploads")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "ListMultipartUploads", mustGetClaimsFromToken(r))
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
if s3Error := checkRequestAuthType(ctx, r, policy.ListBucketMultipartUploadsAction, bucket, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.ListBucketMultipartUploadsAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
prefix, keyMarker, uploadIDMarker, delimiter, maxUploads, _, s3Error := getBucketMultipartResources(r.URL.Query()) prefix, keyMarker, uploadIDMarker, delimiter, maxUploads, _, s3Error := getBucketMultipartResources(r.URL.Query())
if s3Error != ErrNone { if s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
if maxUploads < 0 { if maxUploads < 0 {
writeErrorResponse(w, ErrInvalidMaxUploads, r.URL) writeErrorResponse(w, ErrInvalidMaxUploads, r.URL, guessIsBrowserReq(r))
return return
} }
if keyMarker != "" { if keyMarker != "" {
// Marker not common with prefix is not implemented. // Marker not common with prefix is not implemented.
if !hasPrefix(keyMarker, prefix) { if !hasPrefix(keyMarker, prefix) {
writeErrorResponse(w, ErrNotImplemented, r.URL) writeErrorResponse(w, ErrNotImplemented, r.URL, guessIsBrowserReq(r))
return return
} }
} }
listMultipartsInfo, err := objectAPI.ListMultipartUploads(ctx, bucket, prefix, keyMarker, uploadIDMarker, delimiter, maxUploads) listMultipartsInfo, err := objectAPI.ListMultipartUploads(ctx, bucket, prefix, keyMarker, uploadIDMarker, delimiter, maxUploads)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
// generate response // generate response
@@ -192,11 +192,11 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter,
func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "ListBuckets") ctx := newContext(r, w, "ListBuckets")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "ListBuckets", mustGetClaimsFromToken(r))
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
listBuckets := objectAPI.ListBuckets listBuckets := objectAPI.ListBuckets
@@ -206,7 +206,7 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R
} }
if s3Error := checkRequestAuthType(ctx, r, policy.ListAllMyBucketsAction, "", ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.ListAllMyBucketsAction, "", ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
// If etcd, dns federation configured list buckets from etcd. // If etcd, dns federation configured list buckets from etcd.
@@ -214,7 +214,7 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R
if globalDNSConfig != nil { if globalDNSConfig != nil {
dnsBuckets, err := globalDNSConfig.List() dnsBuckets, err := globalDNSConfig.List()
if err != nil && err != dns.ErrNoEntriesFound { if err != nil && err != dns.ErrNoEntriesFound {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
bucketSet := set.NewStringSet() bucketSet := set.NewStringSet()
@@ -233,7 +233,7 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R
var err error var err error
bucketsInfo, err = listBuckets(ctx) bucketsInfo, err = listBuckets(ctx)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
} }
@@ -250,14 +250,14 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R
func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "DeleteMultipleObjects") ctx := newContext(r, w, "DeleteMultipleObjects")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "DeleteMultipleObjects", mustGetClaimsFromToken(r))
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -266,7 +266,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
// In the event access is denied, a 200 response should still be returned // In the event access is denied, a 200 response should still be returned
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html // http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
if s3Error != ErrAccessDenied { if s3Error != ErrAccessDenied {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
} }
@@ -274,14 +274,14 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
// Content-Length is required and should be non-zero // Content-Length is required and should be non-zero
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html // http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
if r.ContentLength <= 0 { if r.ContentLength <= 0 {
writeErrorResponse(w, ErrMissingContentLength, r.URL) writeErrorResponse(w, ErrMissingContentLength, r.URL, guessIsBrowserReq(r))
return return
} }
// Content-Md5 is requied should be set // Content-Md5 is requied should be set
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html // http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
if _, ok := r.Header["Content-Md5"]; !ok { if _, ok := r.Header["Content-Md5"]; !ok {
writeErrorResponse(w, ErrMissingContentMD5, r.URL) writeErrorResponse(w, ErrMissingContentMD5, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -297,7 +297,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
// Read incoming body XML bytes. // Read incoming body XML bytes.
if _, err := io.ReadFull(r.Body, deleteXMLBytes); err != nil { if _, err := io.ReadFull(r.Body, deleteXMLBytes); err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
writeErrorResponse(w, ErrInternalError, r.URL) writeErrorResponse(w, ErrInternalError, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -305,7 +305,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
deleteObjects := &DeleteObjectsRequest{} deleteObjects := &DeleteObjectsRequest{}
if err := xml.Unmarshal(deleteXMLBytes, deleteObjects); err != nil { if err := xml.Unmarshal(deleteXMLBytes, deleteObjects); err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
writeErrorResponse(w, ErrMalformedXML, r.URL) writeErrorResponse(w, ErrMalformedXML, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -313,7 +313,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
if globalWORMEnabled { if globalWORMEnabled {
// Not required to check whether given objects exist or not, because // Not required to check whether given objects exist or not, because
// DeleteMultipleObject is always successful irrespective of object existence. // DeleteMultipleObject is always successful irrespective of object existence.
writeErrorResponse(w, ErrMethodNotAllowed, r.URL) writeErrorResponse(w, ErrMethodNotAllowed, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -397,11 +397,11 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "PutBucket") ctx := newContext(r, w, "PutBucket")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "PutBucket", mustGetClaimsFromToken(r))
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -409,21 +409,21 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req
bucket := vars["bucket"] bucket := vars["bucket"]
if s3Error := checkRequestAuthType(ctx, r, policy.CreateBucketAction, bucket, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.CreateBucketAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
// Parse incoming location constraint. // Parse incoming location constraint.
location, s3Error := parseLocationConstraint(r) location, s3Error := parseLocationConstraint(r)
if s3Error != ErrNone { if s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
// Validate if location sent by the client is valid, reject // Validate if location sent by the client is valid, reject
// requests which do not follow valid region requirements. // requests which do not follow valid region requirements.
if !isValidLocation(location) { if !isValidLocation(location) {
writeErrorResponse(w, ErrInvalidRegion, r.URL) writeErrorResponse(w, ErrInvalidRegion, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -432,12 +432,12 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req
if err == dns.ErrNoEntriesFound { if err == dns.ErrNoEntriesFound {
// Proceed to creating a bucket. // Proceed to creating a bucket.
if err = objectAPI.MakeBucketWithLocation(ctx, bucket, location); err != nil { if err = objectAPI.MakeBucketWithLocation(ctx, bucket, location); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
if err = globalDNSConfig.Put(bucket); err != nil { if err = globalDNSConfig.Put(bucket); err != nil {
objectAPI.DeleteBucket(ctx, bucket) objectAPI.DeleteBucket(ctx, bucket)
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -447,18 +447,18 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req
writeSuccessResponseHeadersOnly(w) writeSuccessResponseHeadersOnly(w)
return return
} }
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
writeErrorResponse(w, ErrBucketAlreadyOwnedByYou, r.URL) writeErrorResponse(w, ErrBucketAlreadyOwnedByYou, r.URL, guessIsBrowserReq(r))
return return
} }
// Proceed to creating a bucket. // Proceed to creating a bucket.
err := objectAPI.MakeBucketWithLocation(ctx, bucket, location) err := objectAPI.MakeBucketWithLocation(ctx, bucket, location)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -475,11 +475,11 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req
func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "PostPolicyBucket") ctx := newContext(r, w, "PostPolicyBucket")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "PostPolicyBucket", mustGetClaimsFromToken(r))
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -488,17 +488,17 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
// Require Content-Length to be set in the request // Require Content-Length to be set in the request
size := r.ContentLength size := r.ContentLength
if size < 0 { if size < 0 {
writeErrorResponse(w, ErrMissingContentLength, r.URL) writeErrorResponse(w, ErrMissingContentLength, r.URL, guessIsBrowserReq(r))
return return
} }
resource, err := getResource(r.URL.Path, r.Host, globalDomainName) resource, err := getResource(r.URL.Path, r.Host, globalDomainName)
if err != nil { if err != nil {
writeErrorResponse(w, ErrInvalidRequest, r.URL) writeErrorResponse(w, ErrInvalidRequest, r.URL, guessIsBrowserReq(r))
return return
} }
// Make sure that the URL does not contain object name. // Make sure that the URL does not contain object name.
if bucket != filepath.Clean(resource[1:]) { if bucket != filepath.Clean(resource[1:]) {
writeErrorResponse(w, ErrMethodNotAllowed, r.URL) writeErrorResponse(w, ErrMethodNotAllowed, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -507,7 +507,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
reader, err := r.MultipartReader() reader, err := r.MultipartReader()
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL) writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -515,7 +515,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
form, err := reader.ReadForm(maxFormMemory) form, err := reader.ReadForm(maxFormMemory)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL) writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -526,13 +526,13 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
fileBody, fileName, fileSize, formValues, err := extractPostPolicyFormValues(ctx, form) fileBody, fileName, fileSize, formValues, err := extractPostPolicyFormValues(ctx, form)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL) writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL, guessIsBrowserReq(r))
return return
} }
// Check if file is provided, error out otherwise. // Check if file is provided, error out otherwise.
if fileBody == nil { if fileBody == nil {
writeErrorResponse(w, ErrPOSTFileRequired, r.URL) writeErrorResponse(w, ErrPOSTFileRequired, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -554,7 +554,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
if successRedirect != "" { if successRedirect != "" {
redirectURL, err = url.Parse(successRedirect) redirectURL, err = url.Parse(successRedirect)
if err != nil { if err != nil {
writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL) writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL, guessIsBrowserReq(r))
return return
} }
} }
@@ -562,58 +562,63 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
// Verify policy signature. // Verify policy signature.
apiErr := doesPolicySignatureMatch(formValues) apiErr := doesPolicySignatureMatch(formValues)
if apiErr != ErrNone { if apiErr != ErrNone {
writeErrorResponse(w, apiErr, r.URL) writeErrorResponse(w, apiErr, r.URL, guessIsBrowserReq(r))
return return
} }
policyBytes, err := base64.StdEncoding.DecodeString(formValues.Get("Policy")) policyBytes, err := base64.StdEncoding.DecodeString(formValues.Get("Policy"))
if err != nil { if err != nil {
writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL) writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL, guessIsBrowserReq(r))
return return
} }
postPolicyForm, err := parsePostPolicyForm(string(policyBytes)) // Handle policy if it is set.
if err != nil { if len(policyBytes) > 0 {
writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL) postPolicyForm, err := parsePostPolicyForm(string(policyBytes))
return if err != nil {
} writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL, guessIsBrowserReq(r))
// Make sure formValues adhere to policy restrictions.
if apiErr = checkPostPolicy(formValues, postPolicyForm); apiErr != ErrNone {
writeErrorResponse(w, apiErr, r.URL)
return
}
// Ensure that the object size is within expected range, also the file size
// should not exceed the maximum single Put size (5 GiB)
lengthRange := postPolicyForm.Conditions.ContentLengthRange
if lengthRange.Valid {
if fileSize < lengthRange.Min {
writeErrorResponse(w, toAPIErrorCode(ctx, errDataTooSmall), r.URL)
return return
} }
if fileSize > lengthRange.Max || isMaxObjectSize(fileSize) { // Make sure formValues adhere to policy restrictions.
writeErrorResponse(w, toAPIErrorCode(ctx, errDataTooLarge), r.URL) if apiErr = checkPostPolicy(formValues, postPolicyForm); apiErr != ErrNone {
writeErrorResponse(w, apiErr, r.URL, guessIsBrowserReq(r))
return return
} }
// Ensure that the object size is within expected range, also the file size
// should not exceed the maximum single Put size (5 GiB)
lengthRange := postPolicyForm.Conditions.ContentLengthRange
if lengthRange.Valid {
if fileSize < lengthRange.Min {
writeErrorResponse(w, toAPIErrorCode(ctx, errDataTooSmall), r.URL, guessIsBrowserReq(r))
return
}
if fileSize > lengthRange.Max || isMaxObjectSize(fileSize) {
writeErrorResponse(w, toAPIErrorCode(ctx, errDataTooLarge), r.URL, guessIsBrowserReq(r))
return
}
}
} }
// Extract metadata to be saved from received Form. // Extract metadata to be saved from received Form.
metadata := make(map[string]string) metadata := make(map[string]string)
err = extractMetadataFromMap(ctx, formValues, metadata) err = extractMetadataFromMap(ctx, formValues, metadata)
if err != nil { if err != nil {
writeErrorResponse(w, ErrInternalError, r.URL) writeErrorResponse(w, ErrInternalError, r.URL, guessIsBrowserReq(r))
return return
} }
hashReader, err := hash.NewReader(fileBody, fileSize, "", "", fileSize) hashReader, err := hash.NewReader(fileBody, fileSize, "", "", fileSize)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
rawReader := hashReader
pReader := NewPutObjReader(rawReader, nil, nil)
var objectEncryptionKey []byte
if objectAPI.IsEncryptionSupported() { if objectAPI.IsEncryptionSupported() {
if hasServerSideEncryptionHeader(formValues) && !hasSuffix(object, slashSeparator) { // handle SSE-C and SSE-S3 requests if hasServerSideEncryptionHeader(formValues) && !hasSuffix(object, slashSeparator) { // handle SSE-C and SSE-S3 requests
var reader io.Reader var reader io.Reader
@@ -621,27 +626,28 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
if crypto.SSEC.IsRequested(formValues) { if crypto.SSEC.IsRequested(formValues) {
key, err = ParseSSECustomerHeader(formValues) key, err = ParseSSECustomerHeader(formValues)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
} }
reader, err = newEncryptReader(hashReader, key, bucket, object, metadata, crypto.S3.IsRequested(formValues)) reader, objectEncryptionKey, err = newEncryptReader(hashReader, key, bucket, object, metadata, crypto.S3.IsRequested(formValues))
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
info := ObjectInfo{Size: fileSize} info := ObjectInfo{Size: fileSize}
hashReader, err = hash.NewReader(reader, info.EncryptedSize(), "", "", fileSize) // do not try to verify encrypted content hashReader, err = hash.NewReader(reader, info.EncryptedSize(), "", "", fileSize) // do not try to verify encrypted content
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
pReader = NewPutObjReader(rawReader, hashReader, objectEncryptionKey)
} }
} }
objInfo, err := objectAPI.PutObject(ctx, bucket, object, hashReader, metadata, ObjectOptions{}) objInfo, err := objectAPI.PutObject(ctx, bucket, object, pReader, metadata, ObjectOptions{})
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -700,7 +706,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "HeadBucket") ctx := newContext(r, w, "HeadBucket")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "HeadBucket", mustGetClaimsFromToken(r))
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@@ -732,19 +738,19 @@ func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Re
func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "DeleteBucket") ctx := newContext(r, w, "DeleteBucket")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "DeleteBucket", mustGetClaimsFromToken(r))
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
if s3Error := checkRequestAuthType(ctx, r, policy.DeleteBucketAction, bucket, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.DeleteBucketAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -754,7 +760,7 @@ func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.
} }
// Attempt to delete bucket. // Attempt to delete bucket.
if err := deleteBucket(ctx, bucket); err != nil { if err := deleteBucket(ctx, bucket); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -766,7 +772,7 @@ func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.
if err := globalDNSConfig.Delete(bucket); err != nil { if err := globalDNSConfig.Delete(bucket); err != nil {
// Deleting DNS entry failed, attempt to create the bucket again. // Deleting DNS entry failed, attempt to create the bucket again.
objectAPI.MakeBucketWithLocation(ctx, bucket, "") objectAPI.MakeBucketWithLocation(ctx, bucket, "")
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
} }
+1 -1
View File
@@ -625,7 +625,7 @@ func testAPIDeleteMultipleObjectsHandler(obj ObjectLayer, instanceType, bucketNa
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
objectName := "test-object-" + strconv.Itoa(i) objectName := "test-object-" + strconv.Itoa(i)
// uploading the object. // uploading the object.
_, err = obj.PutObject(context.Background(), bucketName, objectName, mustGetHashReader(t, bytes.NewBuffer(contentBytes), int64(len(contentBytes)), "", sha256sum), nil, ObjectOptions{}) _, err = obj.PutObject(context.Background(), bucketName, objectName, mustGetPutObjReader(t, bytes.NewBuffer(contentBytes), int64(len(contentBytes)), "", sha256sum), nil, ObjectOptions{})
// if object upload fails stop the test. // if object upload fails stop the test.
if err != nil { if err != nil {
t.Fatalf("Put Object %d: Error uploading object: <ERROR> %v", i, err) t.Fatalf("Put Object %d: Error uploading object: <ERROR> %v", i, err)
+35 -31
View File
@@ -44,30 +44,30 @@ var errNoSuchNotifications = errors.New("The specified bucket does not have buck
func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "GetBucketNotification") ctx := newContext(r, w, "GetBucketNotification")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "GetBucketNotification", mustGetClaimsFromToken(r))
vars := mux.Vars(r) vars := mux.Vars(r)
bucketName := vars["bucket"] bucketName := vars["bucket"]
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
if !objAPI.IsNotificationSupported() { if !objAPI.IsNotificationSupported() {
writeErrorResponse(w, ErrNotImplemented, r.URL) writeErrorResponse(w, ErrNotImplemented, r.URL, guessIsBrowserReq(r))
return return
} }
if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketNotificationAction, bucketName, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketNotificationAction, bucketName, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
_, err := objAPI.GetBucketInfo(ctx, bucketName) _, err := objAPI.GetBucketInfo(ctx, bucketName)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -76,7 +76,7 @@ func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter,
if err != nil { if err != nil {
// Ignore errNoSuchNotifications to comply with AWS S3. // Ignore errNoSuchNotifications to comply with AWS S3.
if err != errNoSuchNotifications { if err != errNoSuchNotifications {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -90,7 +90,7 @@ func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter,
notificationBytes, err := xml.Marshal(nConfig) notificationBytes, err := xml.Marshal(nConfig)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -102,16 +102,16 @@ func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter,
func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "PutBucketNotification") ctx := newContext(r, w, "PutBucketNotification")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "PutBucketNotification", mustGetClaimsFromToken(r))
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
if !objectAPI.IsNotificationSupported() { if !objectAPI.IsNotificationSupported() {
writeErrorResponse(w, ErrNotImplemented, r.URL) writeErrorResponse(w, ErrNotImplemented, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -119,19 +119,19 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter,
bucketName := vars["bucket"] bucketName := vars["bucket"]
if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketNotificationAction, bucketName, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketNotificationAction, bucketName, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
_, err := objectAPI.GetBucketInfo(ctx, bucketName) _, err := objectAPI.GetBucketInfo(ctx, bucketName)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
// PutBucketNotification always needs a Content-Length. // PutBucketNotification always needs a Content-Length.
if r.ContentLength <= 0 { if r.ContentLength <= 0 {
writeErrorResponse(w, ErrMissingContentLength, r.URL) writeErrorResponse(w, ErrMissingContentLength, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -143,12 +143,12 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter,
apiErr = toAPIErrorCode(ctx, err) apiErr = toAPIErrorCode(ctx, err)
} }
writeErrorResponse(w, apiErr, r.URL) writeErrorResponse(w, apiErr, r.URL, guessIsBrowserReq(r))
return return
} }
if err = saveNotificationConfig(ctx, objectAPI, bucketName, config); err != nil { if err = saveNotificationConfig(ctx, objectAPI, bucketName, config); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -164,24 +164,28 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter,
func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "ListenBucketNotification") ctx := newContext(r, w, "ListenBucketNotification")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "ListenBucketNotification", mustGetClaimsFromToken(r))
// Validate if bucket exists. // Validate if bucket exists.
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
if !objAPI.IsNotificationSupported() { if !objAPI.IsNotificationSupported() {
writeErrorResponse(w, ErrNotImplemented, r.URL) writeErrorResponse(w, ErrNotImplemented, r.URL, guessIsBrowserReq(r))
return return
} }
if !objAPI.IsListenBucketSupported() {
writeErrorResponse(w, ErrNotImplemented, r.URL, guessIsBrowserReq(r))
return
}
vars := mux.Vars(r) vars := mux.Vars(r)
bucketName := vars["bucket"] bucketName := vars["bucket"]
if s3Error := checkRequestAuthType(ctx, r, policy.ListenBucketNotificationAction, bucketName, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.ListenBucketNotificationAction, bucketName, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -189,11 +193,11 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
var prefix string var prefix string
if len(values["prefix"]) > 1 { if len(values["prefix"]) > 1 {
writeErrorResponse(w, ErrFilterNamePrefix, r.URL) writeErrorResponse(w, ErrFilterNamePrefix, r.URL, guessIsBrowserReq(r))
} }
if len(values["prefix"]) == 1 { if len(values["prefix"]) == 1 {
if err := event.ValidateFilterRuleValue(values["prefix"][0]); err != nil { if err := event.ValidateFilterRuleValue(values["prefix"][0]); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -202,11 +206,11 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
var suffix string var suffix string
if len(values["suffix"]) > 1 { if len(values["suffix"]) > 1 {
writeErrorResponse(w, ErrFilterNameSuffix, r.URL) writeErrorResponse(w, ErrFilterNameSuffix, r.URL, guessIsBrowserReq(r))
} }
if len(values["suffix"]) == 1 { if len(values["suffix"]) == 1 {
if err := event.ValidateFilterRuleValue(values["suffix"][0]); err != nil { if err := event.ValidateFilterRuleValue(values["suffix"][0]); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -219,7 +223,7 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
for _, s := range values["events"] { for _, s := range values["events"] {
eventName, err := event.ParseName(s) eventName, err := event.ParseName(s)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -227,19 +231,19 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
} }
if _, err := objAPI.GetBucketInfo(ctx, bucketName); err != nil { if _, err := objAPI.GetBucketInfo(ctx, bucketName); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
host, err := xnet.ParseHost(r.RemoteAddr) host, err := xnet.ParseHost(r.RemoteAddr)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
target, err := target.NewHTTPClientTarget(*host, w) target, err := target.NewHTTPClientTarget(*host, w)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -247,7 +251,7 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
if err = globalNotificationSys.AddRemoteTarget(bucketName, target, rulesMap); err != nil { if err = globalNotificationSys.AddRemoteTarget(bucketName, target, rulesMap); err != nil {
logger.GetReqInfo(ctx).AppendTags("target", target.ID().Name) logger.GetReqInfo(ctx).AppendTags("target", target.ID().Name)
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
defer globalNotificationSys.RemoveRemoteTarget(bucketName, target.ID()) defer globalNotificationSys.RemoveRemoteTarget(bucketName, target.ID())
@@ -255,13 +259,13 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
thisAddr, err := xnet.ParseHost(GetLocalPeer(globalEndpoints)) thisAddr, err := xnet.ParseHost(GetLocalPeer(globalEndpoints))
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
if err = SaveListener(objAPI, bucketName, eventNames, pattern, target.ID(), *thisAddr); err != nil { if err = SaveListener(objAPI, bucketName, eventNames, pattern, target.ID(), *thisAddr); err != nil {
logger.GetReqInfo(ctx).AppendTags("target", target.ID().Name) logger.GetReqInfo(ctx).AppendTags("target", target.ID().Name)
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -271,7 +275,7 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
if err = RemoveListener(objAPI, bucketName, target.ID(), *thisAddr); err != nil { if err = RemoveListener(objAPI, bucketName, target.ID(), *thisAddr); err != nil {
logger.GetReqInfo(ctx).AppendTags("target", target.ID().Name) logger.GetReqInfo(ctx).AppendTags("target", target.ID().Name)
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
} }
+20 -20
View File
@@ -40,11 +40,11 @@ const (
func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "PutBucketPolicy") ctx := newContext(r, w, "PutBucketPolicy")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "PutBucketPolicy", mustGetClaimsFromToken(r))
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -52,43 +52,43 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht
bucket := vars["bucket"] bucket := vars["bucket"]
if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketPolicyAction, bucket, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketPolicyAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
// Check if bucket exists. // Check if bucket exists.
if _, err := objAPI.GetBucketInfo(ctx, bucket); err != nil { if _, err := objAPI.GetBucketInfo(ctx, bucket); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
// Error out if Content-Length is missing. // Error out if Content-Length is missing.
// PutBucketPolicy always needs Content-Length. // PutBucketPolicy always needs Content-Length.
if r.ContentLength <= 0 { if r.ContentLength <= 0 {
writeErrorResponse(w, ErrMissingContentLength, r.URL) writeErrorResponse(w, ErrMissingContentLength, r.URL, guessIsBrowserReq(r))
return return
} }
// Error out if Content-Length is beyond allowed size. // Error out if Content-Length is beyond allowed size.
if r.ContentLength > maxBucketPolicySize { if r.ContentLength > maxBucketPolicySize {
writeErrorResponse(w, ErrEntityTooLarge, r.URL) writeErrorResponse(w, ErrEntityTooLarge, r.URL, guessIsBrowserReq(r))
return return
} }
bucketPolicy, err := policy.ParseConfig(io.LimitReader(r.Body, r.ContentLength), bucket) bucketPolicy, err := policy.ParseConfig(io.LimitReader(r.Body, r.ContentLength), bucket)
if err != nil { if err != nil {
writeErrorResponse(w, ErrMalformedPolicy, r.URL) writeErrorResponse(w, ErrMalformedPolicy, r.URL, guessIsBrowserReq(r))
return return
} }
// Version in policy must not be empty // Version in policy must not be empty
if bucketPolicy.Version == "" { if bucketPolicy.Version == "" {
writeErrorResponse(w, ErrMalformedPolicy, r.URL) writeErrorResponse(w, ErrMalformedPolicy, r.URL, guessIsBrowserReq(r))
return return
} }
if err = objAPI.SetBucketPolicy(ctx, bucket, bucketPolicy); err != nil { if err = objAPI.SetBucketPolicy(ctx, bucket, bucketPolicy); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -103,11 +103,11 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht
func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "DeleteBucketPolicy") ctx := newContext(r, w, "DeleteBucketPolicy")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "DeleteBucketPolicy", mustGetClaimsFromToken(r))
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -115,18 +115,18 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r
bucket := vars["bucket"] bucket := vars["bucket"]
if s3Error := checkRequestAuthType(ctx, r, policy.DeleteBucketPolicyAction, bucket, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.DeleteBucketPolicyAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
// Check if bucket exists. // Check if bucket exists.
if _, err := objAPI.GetBucketInfo(ctx, bucket); err != nil { if _, err := objAPI.GetBucketInfo(ctx, bucket); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
if err := objAPI.DeleteBucketPolicy(ctx, bucket); err != nil { if err := objAPI.DeleteBucketPolicy(ctx, bucket); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
@@ -141,11 +141,11 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r
func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "GetBucketPolicy") ctx := newContext(r, w, "GetBucketPolicy")
defer logger.AuditLog(ctx, w, r) defer logger.AuditLog(w, r, "GetBucketPolicy", mustGetClaimsFromToken(r))
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, ErrServerNotInitialized, r.URL) writeErrorResponse(w, ErrServerNotInitialized, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -153,26 +153,26 @@ func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *ht
bucket := vars["bucket"] bucket := vars["bucket"]
if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketPolicyAction, bucket, ""); s3Error != ErrNone { if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketPolicyAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL) writeErrorResponse(w, s3Error, r.URL, guessIsBrowserReq(r))
return return
} }
// Check if bucket exists. // Check if bucket exists.
if _, err := objAPI.GetBucketInfo(ctx, bucket); err != nil { if _, err := objAPI.GetBucketInfo(ctx, bucket); err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
// Read bucket access policy. // Read bucket access policy.
bucketPolicy, err := objAPI.GetBucketPolicy(ctx, bucket) bucketPolicy, err := objAPI.GetBucketPolicy(ctx, bucket)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
policyData, err := json.Marshal(bucketPolicy) policyData, err := json.Marshal(bucketPolicy)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL) writeErrorResponse(w, toAPIErrorCode(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
+5 -1
View File
@@ -71,9 +71,13 @@ func getRootCAs(certsCAsDir string) (*x509.CertPool, error) {
// Get all CA file names. // Get all CA file names.
var caFiles []string var caFiles []string
fis, err := readDir(certsCAsDir) fis, err := readDir(certsCAsDir)
if err != nil { if err != nil && err != errFileNotFound {
return nil, err return nil, err
} }
// Return success if CA's directory is missing.
if err == errFileNotFound {
return nil, nil
}
for _, fi := range fis { for _, fi := range fis {
// Skip all directories. // Skip all directories.
if hasSuffix(fi, slashSeparator) { if hasSuffix(fi, slashSeparator) {
+2 -1
View File
@@ -223,7 +223,8 @@ func TestGetRootCAs(t *testing.T) {
certCAsDir string certCAsDir string
expectedErr error expectedErr error
}{ }{
{"nonexistent-dir", errFileNotFound}, // ignores non-existent directories.
{"nonexistent-dir", nil},
// Ignores directories. // Ignores directories.
{dir1, nil}, {dir1, nil},
// Ignore empty directory. // Ignore empty directory.
+12 -6
View File
@@ -32,6 +32,8 @@ import (
"github.com/minio/minio-go/pkg/set" "github.com/minio/minio-go/pkg/set"
"github.com/minio/minio/cmd/crypto" "github.com/minio/minio/cmd/crypto"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/cmd/logger/target/console"
"github.com/minio/minio/cmd/logger/target/http"
"github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/auth"
"github.com/minio/minio/pkg/dns" "github.com/minio/minio/pkg/dns"
xnet "github.com/minio/minio/pkg/net" xnet "github.com/minio/minio/pkg/net"
@@ -54,25 +56,25 @@ func loadLoggers() {
auditEndpoint, ok := os.LookupEnv("MINIO_AUDIT_LOGGER_HTTP_ENDPOINT") auditEndpoint, ok := os.LookupEnv("MINIO_AUDIT_LOGGER_HTTP_ENDPOINT")
if ok { if ok {
// Enable audit HTTP logging through ENV. // Enable audit HTTP logging through ENV.
logger.AddAuditTarget(logger.NewHTTP(auditEndpoint, NewCustomHTTPTransport())) logger.AddAuditTarget(http.New(auditEndpoint, NewCustomHTTPTransport()))
} }
loggerEndpoint, ok := os.LookupEnv("MINIO_LOGGER_HTTP_ENDPOINT") loggerEndpoint, ok := os.LookupEnv("MINIO_LOGGER_HTTP_ENDPOINT")
if ok { if ok {
// Enable HTTP logging through ENV. // Enable HTTP logging through ENV.
logger.AddTarget(logger.NewHTTP(loggerEndpoint, NewCustomHTTPTransport())) logger.AddTarget(http.New(loggerEndpoint, NewCustomHTTPTransport()))
} else { } else {
for _, l := range globalServerConfig.Logger.HTTP { for _, l := range globalServerConfig.Logger.HTTP {
if l.Enabled { if l.Enabled {
// Enable http logging // Enable http logging
logger.AddTarget(logger.NewHTTP(l.Endpoint, NewCustomHTTPTransport())) logger.AddTarget(http.New(l.Endpoint, NewCustomHTTPTransport()))
} }
} }
} }
if globalServerConfig.Logger.Console.Enabled { if globalServerConfig.Logger.Console.Enabled {
// Enable console logging // Enable console logging
logger.AddTarget(logger.NewConsole()) logger.AddTarget(console.New())
} }
} }
@@ -219,14 +221,18 @@ func handleCommonEnvVars() {
minioEndpointsEnv, ok := os.LookupEnv("MINIO_PUBLIC_IPS") minioEndpointsEnv, ok := os.LookupEnv("MINIO_PUBLIC_IPS")
if ok { if ok {
minioEndpoints := strings.Split(minioEndpointsEnv, ",") minioEndpoints := strings.Split(minioEndpointsEnv, ",")
globalDomainIPs = set.NewStringSet()
for i, ip := range minioEndpoints { for i, ip := range minioEndpoints {
if net.ParseIP(ip) == nil { if net.ParseIP(ip) == nil {
logger.FatalIf(errInvalidArgument, "Unable to initialize Minio server with invalid MINIO_PUBLIC_IPS[%d]: %s", i, ip) logger.FatalIf(errInvalidArgument, "Unable to initialize Minio server with invalid MINIO_PUBLIC_IPS[%d]: %s", i, ip)
} }
globalDomainIPs.Add(ip)
} }
updateDomainIPs(set.CreateStringSet(minioEndpoints...))
} else {
// Add found interfaces IP address to global domain IPS,
// loopback addresses will be naturally dropped.
updateDomainIPs(localIP4)
} }
if globalDomainName != "" && !globalDomainIPs.IsEmpty() && globalEtcdClient != nil { if globalDomainName != "" && !globalDomainIPs.IsEmpty() && globalEtcdClient != nil {
var err error var err error
globalDNSConfig, err = dns.NewCoreDNS(globalDomainName, globalDomainIPs, globalMinioPort, globalEtcdClient) globalDNSConfig, err = dns.NewCoreDNS(globalDomainName, globalDomainIPs, globalMinioPort, globalEtcdClient)
+1 -1
View File
@@ -70,7 +70,7 @@ func saveConfig(ctx context.Context, objAPI ObjectLayer, configFile string, data
return err return err
} }
_, err = objAPI.PutObject(ctx, minioMetaBucket, configFile, hashReader, nil, ObjectOptions{}) _, err = objAPI.PutObject(ctx, minioMetaBucket, configFile, NewPutObjReader(hashReader, nil, nil), nil, ObjectOptions{})
return err return err
} }
+6 -4
View File
@@ -43,9 +43,9 @@ import (
// 6. Make changes in config-current_test.go for any test change // 6. Make changes in config-current_test.go for any test change
// Config version // Config version
const serverConfigVersion = "32" const serverConfigVersion = "33"
type serverConfig = serverConfigV32 type serverConfig = serverConfigV33
var ( var (
// globalServerConfig server config. // globalServerConfig server config.
@@ -556,8 +556,10 @@ func (s *serverConfig) loadToCachedConfigs() {
if globalPolicyOPA == nil { if globalPolicyOPA == nil {
if s.Policy.OPA.URL != nil && s.Policy.OPA.URL.String() != "" { if s.Policy.OPA.URL != nil && s.Policy.OPA.URL.String() != "" {
globalPolicyOPA = iampolicy.NewOpa(iampolicy.OpaArgs{ globalPolicyOPA = iampolicy.NewOpa(iampolicy.OpaArgs{
URL: s.Policy.OPA.URL, URL: s.Policy.OPA.URL,
AuthToken: s.Policy.OPA.AuthToken, AuthToken: s.Policy.OPA.AuthToken,
Transport: NewCustomHTTPTransport(),
CloseRespFn: CloseResponse,
}) })
} }
} }
+1 -1
View File
@@ -188,7 +188,7 @@ func TestValidateConfig(t *testing.T) {
{`{"version": "` + v + `", "credential": { "accessKey": "minio", "secretKey": "minio123" }, "region": "us-east-1", "browser": "on", "notify": { "amqp": { "1": { "enable": true, "url": "", "exchange": "", "routingKey": "", "exchangeType": "", "mandatory": false, "immediate": false, "durable": false, "internal": false, "noWait": false, "autoDeleted": false }}}}`, false}, {`{"version": "` + v + `", "credential": { "accessKey": "minio", "secretKey": "minio123" }, "region": "us-east-1", "browser": "on", "notify": { "amqp": { "1": { "enable": true, "url": "", "exchange": "", "routingKey": "", "exchangeType": "", "mandatory": false, "immediate": false, "durable": false, "internal": false, "noWait": false, "autoDeleted": false }}}}`, false},
// Test 12 - Test NATS // Test 12 - Test NATS
{`{"version": "` + v + `", "credential": { "accessKey": "minio", "secretKey": "minio123" }, "region": "us-east-1", "browser": "on", "notify": { "nats": { "1": { "enable": true, "address": "", "subject": "", "username": "", "password": "", "token": "", "secure": false, "pingInterval": 0, "streaming": { "enable": false, "clusterID": "", "clientID": "", "async": false, "maxPubAcksInflight": 0 } } }}}`, false}, {`{"version": "` + v + `", "credential": { "accessKey": "minio", "secretKey": "minio123" }, "region": "us-east-1", "browser": "on", "notify": { "nats": { "1": { "enable": true, "address": "", "subject": "", "username": "", "password": "", "token": "", "secure": false, "pingInterval": 0, "streaming": { "enable": false, "clusterID": "", "async": false, "maxPubAcksInflight": 0 } } }}}`, false},
// Test 13 - Test ElasticSearch // Test 13 - Test ElasticSearch
{`{"version": "` + v + `", "credential": { "accessKey": "minio", "secretKey": "minio123" }, "region": "us-east-1", "browser": "on", "notify": { "elasticsearch": { "1": { "enable": true, "url": "", "index": "" } }}}`, false}, {`{"version": "` + v + `", "credential": { "accessKey": "minio", "secretKey": "minio123" }, "region": "us-east-1", "browser": "on", "notify": { "elasticsearch": { "1": { "enable": true, "url": "", "index": "" } }}}`, false},
+9 -1
View File
@@ -74,7 +74,15 @@ func (config *ConfigDir) GetCADir() string {
// Create - creates configuration directory tree. // Create - creates configuration directory tree.
func (config *ConfigDir) Create() error { func (config *ConfigDir) Create() error {
return os.MkdirAll(config.GetCADir(), 0700) err := os.MkdirAll(config.GetCADir(), 0700)
// It is possible in kubernetes like deployments this directory
// is already mounted and is not writable, ignore any write errors.
if err != nil {
if os.IsPermission(err) {
err = nil
}
}
return err
} }
// GetMinioConfigFile - returns absolute path of config.json file. // GetMinioConfigFile - returns absolute path of config.json file.
+94 -2
View File
@@ -2413,8 +2413,64 @@ func migrateV27ToV28() error {
return nil return nil
} }
// Migrates '.minio.sys/config.json' to v32. // Migrates ${HOME}/.minio/config.json to '<export_path>/.minio.sys/config/config.json'
func migrateConfigToMinioSys(objAPI ObjectLayer) (err error) {
defer func() {
// Rename config.json to config.json.deprecated only upon
// success of this function.
if err == nil {
os.Rename(getConfigFile(), getConfigFile()+".deprecated")
}
}()
configFile := path.Join(minioConfigPrefix, minioConfigFile)
// Construct path to config.json for the given bucket.
transactionConfigFile := configFile + ".transaction"
// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
// and configFile, take a transaction lock to avoid data race between readConfig()
// and saveConfig().
objLock := globalNSMutex.NewNSLock(minioMetaBucket, transactionConfigFile)
if err = objLock.GetLock(globalOperationTimeout); err != nil {
return err
}
defer objLock.Unlock()
// Verify if backend already has the file.
if err = checkConfig(context.Background(), objAPI, configFile); err != errConfigNotFound {
return err
} // if errConfigNotFound proceed to migrate..
var config = &serverConfig{}
if _, err = Load(getConfigFile(), config); err != nil {
if !os.IsNotExist(err) {
return err
}
// Read from deprecate file as well if necessary.
if _, err = Load(getConfigFile()+".deprecated", config); err != nil {
return err
}
}
return saveServerConfig(context.Background(), objAPI, config)
}
// Migrates '.minio.sys/config.json' to v33.
func migrateMinioSysConfig(objAPI ObjectLayer) error { func migrateMinioSysConfig(objAPI ObjectLayer) error {
configFile := path.Join(minioConfigPrefix, minioConfigFile)
// Construct path to config.json for the given bucket.
transactionConfigFile := configFile + ".transaction"
// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
// and configFile, take a transaction lock to avoid data race between readConfig()
// and saveConfig().
objLock := globalNSMutex.NewNSLock(minioMetaBucket, transactionConfigFile)
if err := objLock.GetLock(globalOperationTimeout); err != nil {
return err
}
defer objLock.Unlock()
if err := migrateV27ToV28MinioSys(objAPI); err != nil { if err := migrateV27ToV28MinioSys(objAPI); err != nil {
return err return err
} }
@@ -2427,7 +2483,10 @@ func migrateMinioSysConfig(objAPI ObjectLayer) error {
if err := migrateV30ToV31MinioSys(objAPI); err != nil { if err := migrateV30ToV31MinioSys(objAPI); err != nil {
return err return err
} }
return migrateV31ToV32MinioSys(objAPI) if err := migrateV31ToV32MinioSys(objAPI); err != nil {
return err
}
return migrateV32ToV33MinioSys(objAPI)
} }
func checkConfigVersion(objAPI ObjectLayer, configFile string, version string) (bool, []byte, error) { func checkConfigVersion(objAPI ObjectLayer, configFile string, version string) (bool, []byte, error) {
@@ -2623,3 +2682,36 @@ func migrateV31ToV32MinioSys(objAPI ObjectLayer) error {
logger.Info(configMigrateMSGTemplate, configFile, "31", "32") logger.Info(configMigrateMSGTemplate, configFile, "31", "32")
return nil return nil
} }
func migrateV32ToV33MinioSys(objAPI ObjectLayer) error {
configFile := path.Join(minioConfigPrefix, minioConfigFile)
ok, data, err := checkConfigVersion(objAPI, configFile, "32")
if err == errConfigNotFound {
return nil
} else if err != nil {
return fmt.Errorf("Unable to load config file. %v", err)
}
if !ok {
return nil
}
cfg := &serverConfigV33{}
if err = json.Unmarshal(data, cfg); err != nil {
return err
}
cfg.Version = "33"
data, err = json.Marshal(cfg)
if err != nil {
return err
}
if err = saveConfig(context.Background(), objAPI, configFile, data); err != nil {
return fmt.Errorf("Failed to migrate config from 32 to 33 . %v", err)
}
logger.Info(configMigrateMSGTemplate, configFile, "32", "33")
return nil
}
+2 -2
View File
@@ -159,8 +159,8 @@ func TestServerConfigMigrateInexistentConfig(t *testing.T) {
} }
} }
// Test if a config migration from v2 to v30 is successfully done // Test if a config migration from v2 to v33 is successfully done
func TestServerConfigMigrateV2toV30(t *testing.T) { func TestServerConfigMigrateV2toV33(t *testing.T) {
rootPath, err := ioutil.TempDir(globalTestTmpDir, "minio-") rootPath, err := ioutil.TempDir(globalTestTmpDir, "minio-")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
+44
View File
@@ -892,3 +892,47 @@ type serverConfigV32 struct {
// Add new external policy enforcements here. // Add new external policy enforcements here.
} `json:"policy"` } `json:"policy"`
} }
// serverConfigV33 is just like version '32', removes clientID from NATS.
type serverConfigV33 struct {
quick.Config `json:"-"` // ignore interfaces
Version string `json:"version"`
// S3 API configuration.
Credential auth.Credentials `json:"credential"`
Region string `json:"region"`
Worm BoolFlag `json:"worm"`
// Storage class configuration
StorageClass storageClassConfig `json:"storageclass"`
// Cache configuration
Cache CacheConfig `json:"cache"`
// KMS configuration
KMS crypto.KMSConfig `json:"kms"`
// Notification queue configuration.
Notify notifier `json:"notify"`
// Logger configuration
Logger loggerConfig `json:"logger"`
// Compression configuration
Compression compressionConfig `json:"compress"`
// OpenID configuration
OpenID struct {
// JWKS validator config.
JWKS validator.JWKSArgs `json:"jwks"`
} `json:"openid"`
// External policy enforcements.
Policy struct {
// OPA configuration.
OPA iampolicy.OpaArgs `json:"opa"`
// Add new external policy enforcements here.
} `json:"policy"`
}
-24
View File
@@ -156,30 +156,6 @@ func NewConfigSys() *ConfigSys {
return &ConfigSys{} return &ConfigSys{}
} }
// Migrates ${HOME}/.minio/config.json to '<export_path>/.minio.sys/config/config.json'
func migrateConfigToMinioSys(objAPI ObjectLayer) error {
defer os.Rename(getConfigFile(), getConfigFile()+".deprecated")
configFile := path.Join(minioConfigPrefix, minioConfigFile)
// Verify if backend already has the file.
if err := checkConfig(context.Background(), objAPI, configFile); err != errConfigNotFound {
return err
} // if errConfigNotFound proceed to migrate..
var config = &serverConfig{}
if _, err := Load(getConfigFile(), config); err != nil {
if !os.IsNotExist(err) {
return err
}
// Read from deprecate file as well if necessary.
if _, err = Load(getConfigFile()+".deprecated", config); err != nil {
return err
}
}
return saveServerConfig(context.Background(), objAPI, config)
}
// Initialize and load config from remote etcd or local config directory // Initialize and load config from remote etcd or local config directory
func initConfig(objAPI ObjectLayer) error { func initConfig(objAPI ObjectLayer) error {
if objAPI == nil { if objAPI == nil {
+4 -4
View File
@@ -22,16 +22,16 @@ import (
) )
// Writes S3 compatible copy part range error. // Writes S3 compatible copy part range error.
func writeCopyPartErr(w http.ResponseWriter, err error, url *url.URL) { func writeCopyPartErr(w http.ResponseWriter, err error, url *url.URL, browser bool) {
switch err { switch err {
case errInvalidRange: case errInvalidRange:
writeErrorResponse(w, ErrInvalidCopyPartRange, url) writeErrorResponse(w, ErrInvalidCopyPartRange, url, browser)
return return
case errInvalidRangeSource: case errInvalidRangeSource:
writeErrorResponse(w, ErrInvalidCopyPartRangeSource, url) writeErrorResponse(w, ErrInvalidCopyPartRangeSource, url, browser)
return return
default: default:
writeErrorResponse(w, ErrInternalError, url) writeErrorResponse(w, ErrInternalError, url, browser)
return return
} }
} }
+5
View File
@@ -192,6 +192,11 @@ func NewVault(kmsConf KMSConfig) (KMS, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ns, ok := os.LookupEnv("MINIO_SSE_VAULT_NAMESPACE"); ok {
c.SetNamespace(ns)
}
accessToken, leaseDuration, err := getVaultAccessToken(c, config.Auth.AppRole.ID, config.Auth.AppRole.Secret) accessToken, leaseDuration, err := getVaultAccessToken(c, config.Auth.AppRole.ID, config.Auth.AppRole.Secret)
if err != nil { if err != nil {
return nil, err return nil, err
+3 -3
View File
@@ -30,7 +30,6 @@ import (
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/disk" "github.com/minio/minio/pkg/disk"
"github.com/minio/minio/pkg/hash"
"github.com/minio/minio/pkg/lock" "github.com/minio/minio/pkg/lock"
) )
@@ -258,7 +257,7 @@ func (cfs *cacheFSObjects) IsOnline() bool {
} }
// Caches the object to disk // Caches the object to disk
func (cfs *cacheFSObjects) Put(ctx context.Context, bucket, object string, data *hash.Reader, metadata map[string]string, opts ObjectOptions) error { func (cfs *cacheFSObjects) Put(ctx context.Context, bucket, object string, data *PutObjReader, metadata map[string]string, opts ObjectOptions) error {
if cfs.diskUsageHigh() { if cfs.diskUsageHigh() {
select { select {
case cfs.purgeChan <- struct{}{}: case cfs.purgeChan <- struct{}{}:
@@ -301,7 +300,8 @@ func (cfs *cacheFSObjects) Exists(ctx context.Context, bucket, object string) bo
// Identical to fs PutObject operation except that it uses ETag in metadata // Identical to fs PutObject operation except that it uses ETag in metadata
// headers. // headers.
func (cfs *cacheFSObjects) PutObject(ctx context.Context, bucket string, object string, data *hash.Reader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, retErr error) { func (cfs *cacheFSObjects) PutObject(ctx context.Context, bucket string, object string, r *PutObjReader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, retErr error) {
data := r.Reader
fs := cfs.FSObjects fs := cfs.FSObjects
// Lock the object. // Lock the object.
objectLock := fs.nsMutex.NewNSLock(bucket, object) objectLock := fs.nsMutex.NewNSLock(bucket, object)
+32 -30
View File
@@ -60,16 +60,16 @@ type cacheObjects struct {
GetObjectNInfoFn func(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) GetObjectNInfoFn func(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error)
GetObjectFn func(ctx context.Context, bucket, object string, startOffset int64, length int64, writer io.Writer, etag string, opts ObjectOptions) (err error) GetObjectFn func(ctx context.Context, bucket, object string, startOffset int64, length int64, writer io.Writer, etag string, opts ObjectOptions) (err error)
GetObjectInfoFn func(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error) GetObjectInfoFn func(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error)
PutObjectFn func(ctx context.Context, bucket, object string, data *hash.Reader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, err error) PutObjectFn func(ctx context.Context, bucket, object string, data *PutObjReader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, err error)
DeleteObjectFn func(ctx context.Context, bucket, object string) error DeleteObjectFn func(ctx context.Context, bucket, object string) error
ListObjectsFn func(ctx context.Context, bucket, prefix, marker, delimiter string, maxKeys int) (result ListObjectsInfo, err error) ListObjectsFn func(ctx context.Context, bucket, prefix, marker, delimiter string, maxKeys int) (result ListObjectsInfo, err error)
ListObjectsV2Fn func(ctx context.Context, bucket, prefix, continuationToken, delimiter string, maxKeys int, fetchOwner bool, startAfter string) (result ListObjectsV2Info, err error) ListObjectsV2Fn func(ctx context.Context, bucket, prefix, continuationToken, delimiter string, maxKeys int, fetchOwner bool, startAfter string) (result ListObjectsV2Info, err error)
ListBucketsFn func(ctx context.Context) (buckets []BucketInfo, err error) ListBucketsFn func(ctx context.Context) (buckets []BucketInfo, err error)
GetBucketInfoFn func(ctx context.Context, bucket string) (bucketInfo BucketInfo, err error) GetBucketInfoFn func(ctx context.Context, bucket string) (bucketInfo BucketInfo, err error)
NewMultipartUploadFn func(ctx context.Context, bucket, object string, metadata map[string]string, opts ObjectOptions) (uploadID string, err error) NewMultipartUploadFn func(ctx context.Context, bucket, object string, metadata map[string]string, opts ObjectOptions) (uploadID string, err error)
PutObjectPartFn func(ctx context.Context, bucket, object, uploadID string, partID int, data *hash.Reader, opts ObjectOptions) (info PartInfo, err error) PutObjectPartFn func(ctx context.Context, bucket, object, uploadID string, partID int, data *PutObjReader, opts ObjectOptions) (info PartInfo, err error)
AbortMultipartUploadFn func(ctx context.Context, bucket, object, uploadID string) error AbortMultipartUploadFn func(ctx context.Context, bucket, object, uploadID string) error
CompleteMultipartUploadFn func(ctx context.Context, bucket, object, uploadID string, uploadedParts []CompletePart) (objInfo ObjectInfo, err error) CompleteMultipartUploadFn func(ctx context.Context, bucket, object, uploadID string, uploadedParts []CompletePart, opts ObjectOptions) (objInfo ObjectInfo, err error)
DeleteBucketFn func(ctx context.Context, bucket string) error DeleteBucketFn func(ctx context.Context, bucket string) error
} }
@@ -92,14 +92,14 @@ type CacheObjectLayer interface {
GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error)
GetObject(ctx context.Context, bucket, object string, startOffset int64, length int64, writer io.Writer, etag string, opts ObjectOptions) (err error) GetObject(ctx context.Context, bucket, object string, startOffset int64, length int64, writer io.Writer, etag string, opts ObjectOptions) (err error)
GetObjectInfo(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error) GetObjectInfo(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error)
PutObject(ctx context.Context, bucket, object string, data *hash.Reader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, err error) PutObject(ctx context.Context, bucket, object string, data *PutObjReader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, err error)
DeleteObject(ctx context.Context, bucket, object string) error DeleteObject(ctx context.Context, bucket, object string) error
// Multipart operations. // Multipart operations.
NewMultipartUpload(ctx context.Context, bucket, object string, metadata map[string]string, opts ObjectOptions) (uploadID string, err error) NewMultipartUpload(ctx context.Context, bucket, object string, metadata map[string]string, opts ObjectOptions) (uploadID string, err error)
PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, data *hash.Reader, opts ObjectOptions) (info PartInfo, err error) PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, data *PutObjReader, opts ObjectOptions) (info PartInfo, err error)
AbortMultipartUpload(ctx context.Context, bucket, object, uploadID string) error AbortMultipartUpload(ctx context.Context, bucket, object, uploadID string) error
CompleteMultipartUpload(ctx context.Context, bucket, object, uploadID string, uploadedParts []CompletePart) (objInfo ObjectInfo, err error) CompleteMultipartUpload(ctx context.Context, bucket, object, uploadID string, uploadedParts []CompletePart, opts ObjectOptions) (objInfo ObjectInfo, err error)
// Storage operations. // Storage operations.
StorageInfo(ctx context.Context) CacheStorageInfo StorageInfo(ctx context.Context) CacheStorageInfo
@@ -247,7 +247,7 @@ func (c cacheObjects) GetObjectNInfo(ctx context.Context, bucket, object string,
go func() { go func() {
opts := ObjectOptions{} opts := ObjectOptions{}
putErr := dcache.Put(ctx, bucket, object, hashReader, c.getMetadata(bkReader.ObjInfo), opts) putErr := dcache.Put(ctx, bucket, object, NewPutObjReader(hashReader, nil, nil), c.getMetadata(bkReader.ObjInfo), opts)
// close the write end of the pipe, so the error gets // close the write end of the pipe, so the error gets
// propagated to getObjReader // propagated to getObjReader
pipeWriter.CloseWithError(putErr) pipeWriter.CloseWithError(putErr)
@@ -303,7 +303,7 @@ func (c cacheObjects) GetObject(ctx context.Context, bucket, object string, star
} }
dcache.Delete(ctx, bucket, object) dcache.Delete(ctx, bucket, object)
} }
if startOffset != 0 || length != objInfo.Size { if startOffset != 0 || (length > 0 && length != objInfo.Size) {
// We don't cache partial objects. // We don't cache partial objects.
return GetObjectFn(ctx, bucket, object, startOffset, length, writer, etag, opts) return GetObjectFn(ctx, bucket, object, startOffset, length, writer, etag, opts)
} }
@@ -320,7 +320,7 @@ func (c cacheObjects) GetObject(ctx context.Context, bucket, object string, star
gerr := GetObjectFn(ctx, bucket, object, 0, objInfo.Size, io.MultiWriter(writer, pipeWriter), etag, opts) gerr := GetObjectFn(ctx, bucket, object, 0, objInfo.Size, io.MultiWriter(writer, pipeWriter), etag, opts)
pipeWriter.CloseWithError(gerr) // Close writer explicitly signaling we wrote all data. pipeWriter.CloseWithError(gerr) // Close writer explicitly signaling we wrote all data.
}() }()
err = dcache.Put(ctx, bucket, object, hashReader, c.getMetadata(objInfo), opts) err = dcache.Put(ctx, bucket, object, NewPutObjReader(hashReader, nil, nil), c.getMetadata(objInfo), opts)
if err != nil { if err != nil {
return err return err
} }
@@ -644,8 +644,9 @@ func (c cacheObjects) isCacheExclude(bucket, object string) bool {
} }
// PutObject - caches the uploaded object for single Put operations // PutObject - caches the uploaded object for single Put operations
func (c cacheObjects) PutObject(ctx context.Context, bucket, object string, r *hash.Reader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, err error) { func (c cacheObjects) PutObject(ctx context.Context, bucket, object string, r *PutObjReader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, err error) {
putObjectFn := c.PutObjectFn putObjectFn := c.PutObjectFn
data := r.Reader
dcache, err := c.cache.getCacheFS(ctx, bucket, object) dcache, err := c.cache.getCacheFS(ctx, bucket, object)
if err != nil { if err != nil {
// disk cache could not be located,execute backend call. // disk cache could not be located,execute backend call.
@@ -666,20 +667,20 @@ func (c cacheObjects) PutObject(ctx context.Context, bucket, object string, r *h
objInfo = ObjectInfo{} objInfo = ObjectInfo{}
// Initialize pipe to stream data to backend // Initialize pipe to stream data to backend
pipeReader, pipeWriter := io.Pipe() pipeReader, pipeWriter := io.Pipe()
hashReader, err := hash.NewReader(pipeReader, size, r.MD5HexString(), r.SHA256HexString(), r.ActualSize()) hashReader, err := hash.NewReader(pipeReader, size, data.MD5HexString(), data.SHA256HexString(), data.ActualSize())
if err != nil { if err != nil {
return ObjectInfo{}, err return ObjectInfo{}, err
} }
// Initialize pipe to stream data to cache // Initialize pipe to stream data to cache
rPipe, wPipe := io.Pipe() rPipe, wPipe := io.Pipe()
cHashReader, err := hash.NewReader(rPipe, size, r.MD5HexString(), r.SHA256HexString(), r.ActualSize()) cHashReader, err := hash.NewReader(rPipe, size, data.MD5HexString(), data.SHA256HexString(), data.ActualSize())
if err != nil { if err != nil {
return ObjectInfo{}, err return ObjectInfo{}, err
} }
oinfoCh := make(chan ObjectInfo) oinfoCh := make(chan ObjectInfo)
errCh := make(chan error) errCh := make(chan error)
go func() { go func() {
oinfo, perr := putObjectFn(ctx, bucket, object, hashReader, metadata, opts) oinfo, perr := putObjectFn(ctx, bucket, object, NewPutObjReader(hashReader, nil, nil), metadata, opts)
if perr != nil { if perr != nil {
pipeWriter.CloseWithError(perr) pipeWriter.CloseWithError(perr)
wPipe.CloseWithError(perr) wPipe.CloseWithError(perr)
@@ -692,14 +693,14 @@ func (c cacheObjects) PutObject(ctx context.Context, bucket, object string, r *h
}() }()
go func() { go func() {
if err = dcache.Put(ctx, bucket, object, cHashReader, metadata, opts); err != nil { if err = dcache.Put(ctx, bucket, object, NewPutObjReader(cHashReader, nil, nil), metadata, opts); err != nil {
wPipe.CloseWithError(err) wPipe.CloseWithError(err)
return return
} }
}() }()
mwriter := io.MultiWriter(pipeWriter, wPipe) mwriter := io.MultiWriter(pipeWriter, wPipe)
_, err = io.Copy(mwriter, r) _, err = io.Copy(mwriter, data)
if err != nil { if err != nil {
err = <-errCh err = <-errCh
return objInfo, err return objInfo, err
@@ -734,16 +735,17 @@ func (c cacheObjects) NewMultipartUpload(ctx context.Context, bucket, object str
} }
// PutObjectPart - uploads part to backend and cache simultaneously. // PutObjectPart - uploads part to backend and cache simultaneously.
func (c cacheObjects) PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, data *hash.Reader, opts ObjectOptions) (info PartInfo, err error) { func (c cacheObjects) PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, r *PutObjReader, opts ObjectOptions) (info PartInfo, err error) {
data := r.Reader
putObjectPartFn := c.PutObjectPartFn putObjectPartFn := c.PutObjectPartFn
dcache, err := c.cache.getCacheFS(ctx, bucket, object) dcache, err := c.cache.getCacheFS(ctx, bucket, object)
if err != nil { if err != nil {
// disk cache could not be located,execute backend call. // disk cache could not be located,execute backend call.
return putObjectPartFn(ctx, bucket, object, uploadID, partID, data, opts) return putObjectPartFn(ctx, bucket, object, uploadID, partID, r, opts)
} }
if c.isCacheExclude(bucket, object) { if c.isCacheExclude(bucket, object) {
return putObjectPartFn(ctx, bucket, object, uploadID, partID, data, opts) return putObjectPartFn(ctx, bucket, object, uploadID, partID, r, opts)
} }
// make sure cache has at least size space available // make sure cache has at least size space available
@@ -753,7 +755,7 @@ func (c cacheObjects) PutObjectPart(ctx context.Context, bucket, object, uploadI
case dcache.purgeChan <- struct{}{}: case dcache.purgeChan <- struct{}{}:
default: default:
} }
return putObjectPartFn(ctx, bucket, object, uploadID, partID, data, opts) return putObjectPartFn(ctx, bucket, object, uploadID, partID, r, opts)
} }
info = PartInfo{} info = PartInfo{}
@@ -772,7 +774,7 @@ func (c cacheObjects) PutObjectPart(ctx context.Context, bucket, object, uploadI
pinfoCh := make(chan PartInfo) pinfoCh := make(chan PartInfo)
errorCh := make(chan error) errorCh := make(chan error)
go func() { go func() {
info, err = putObjectPartFn(ctx, bucket, object, uploadID, partID, hashReader, opts) info, err = putObjectPartFn(ctx, bucket, object, uploadID, partID, NewPutObjReader(hashReader, nil, nil), opts)
if err != nil { if err != nil {
close(pinfoCh) close(pinfoCh)
pipeWriter.CloseWithError(err) pipeWriter.CloseWithError(err)
@@ -784,7 +786,7 @@ func (c cacheObjects) PutObjectPart(ctx context.Context, bucket, object, uploadI
pinfoCh <- info pinfoCh <- info
}() }()
go func() { go func() {
if _, perr := dcache.PutObjectPart(ctx, bucket, object, uploadID, partID, cHashReader, opts); perr != nil { if _, perr := dcache.PutObjectPart(ctx, bucket, object, uploadID, partID, NewPutObjReader(cHashReader, nil, nil), opts); perr != nil {
wPipe.CloseWithError(perr) wPipe.CloseWithError(perr)
return return
} }
@@ -826,25 +828,25 @@ func (c cacheObjects) AbortMultipartUpload(ctx context.Context, bucket, object,
} }
// CompleteMultipartUpload - completes multipart upload operation on backend and cache. // CompleteMultipartUpload - completes multipart upload operation on backend and cache.
func (c cacheObjects) CompleteMultipartUpload(ctx context.Context, bucket, object, uploadID string, uploadedParts []CompletePart) (objInfo ObjectInfo, err error) { func (c cacheObjects) CompleteMultipartUpload(ctx context.Context, bucket, object, uploadID string, uploadedParts []CompletePart, opts ObjectOptions) (objInfo ObjectInfo, err error) {
completeMultipartUploadFn := c.CompleteMultipartUploadFn completeMultipartUploadFn := c.CompleteMultipartUploadFn
if c.isCacheExclude(bucket, object) { if c.isCacheExclude(bucket, object) {
return completeMultipartUploadFn(ctx, bucket, object, uploadID, uploadedParts) return completeMultipartUploadFn(ctx, bucket, object, uploadID, uploadedParts, opts)
} }
dcache, err := c.cache.getCacheFS(ctx, bucket, object) dcache, err := c.cache.getCacheFS(ctx, bucket, object)
if err != nil { if err != nil {
// disk cache could not be located,execute backend call. // disk cache could not be located,execute backend call.
return completeMultipartUploadFn(ctx, bucket, object, uploadID, uploadedParts) return completeMultipartUploadFn(ctx, bucket, object, uploadID, uploadedParts, opts)
} }
// perform backend operation // perform backend operation
objInfo, err = completeMultipartUploadFn(ctx, bucket, object, uploadID, uploadedParts) objInfo, err = completeMultipartUploadFn(ctx, bucket, object, uploadID, uploadedParts, opts)
if err != nil { if err != nil {
return return
} }
// create new multipart upload in cache with same uploadID // create new multipart upload in cache with same uploadID
dcache.CompleteMultipartUpload(ctx, bucket, object, uploadID, uploadedParts) dcache.CompleteMultipartUpload(ctx, bucket, object, uploadID, uploadedParts, opts)
return return
} }
@@ -969,7 +971,7 @@ func newServerCacheObjects(config CacheConfig) (CacheObjectLayer, error) {
GetObjectNInfoFn: func(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) { GetObjectNInfoFn: func(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) {
return newObjectLayerFn().GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts) return newObjectLayerFn().GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts)
}, },
PutObjectFn: func(ctx context.Context, bucket, object string, data *hash.Reader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, err error) { PutObjectFn: func(ctx context.Context, bucket, object string, data *PutObjReader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, err error) {
return newObjectLayerFn().PutObject(ctx, bucket, object, data, metadata, opts) return newObjectLayerFn().PutObject(ctx, bucket, object, data, metadata, opts)
}, },
DeleteObjectFn: func(ctx context.Context, bucket, object string) error { DeleteObjectFn: func(ctx context.Context, bucket, object string) error {
@@ -990,14 +992,14 @@ func newServerCacheObjects(config CacheConfig) (CacheObjectLayer, error) {
NewMultipartUploadFn: func(ctx context.Context, bucket, object string, metadata map[string]string, opts ObjectOptions) (uploadID string, err error) { NewMultipartUploadFn: func(ctx context.Context, bucket, object string, metadata map[string]string, opts ObjectOptions) (uploadID string, err error) {
return newObjectLayerFn().NewMultipartUpload(ctx, bucket, object, metadata, opts) return newObjectLayerFn().NewMultipartUpload(ctx, bucket, object, metadata, opts)
}, },
PutObjectPartFn: func(ctx context.Context, bucket, object, uploadID string, partID int, data *hash.Reader, opts ObjectOptions) (info PartInfo, err error) { PutObjectPartFn: func(ctx context.Context, bucket, object, uploadID string, partID int, data *PutObjReader, opts ObjectOptions) (info PartInfo, err error) {
return newObjectLayerFn().PutObjectPart(ctx, bucket, object, uploadID, partID, data, opts) return newObjectLayerFn().PutObjectPart(ctx, bucket, object, uploadID, partID, data, opts)
}, },
AbortMultipartUploadFn: func(ctx context.Context, bucket, object, uploadID string) error { AbortMultipartUploadFn: func(ctx context.Context, bucket, object, uploadID string) error {
return newObjectLayerFn().AbortMultipartUpload(ctx, bucket, object, uploadID) return newObjectLayerFn().AbortMultipartUpload(ctx, bucket, object, uploadID)
}, },
CompleteMultipartUploadFn: func(ctx context.Context, bucket, object, uploadID string, uploadedParts []CompletePart) (objInfo ObjectInfo, err error) { CompleteMultipartUploadFn: func(ctx context.Context, bucket, object, uploadID string, uploadedParts []CompletePart, opts ObjectOptions) (objInfo ObjectInfo, err error) {
return newObjectLayerFn().CompleteMultipartUpload(ctx, bucket, object, uploadID, uploadedParts) return newObjectLayerFn().CompleteMultipartUpload(ctx, bucket, object, uploadID, uploadedParts, opts)
}, },
DeleteBucketFn: func(ctx context.Context, bucket string) error { DeleteBucketFn: func(ctx context.Context, bucket string) error {
return newObjectLayerFn().DeleteBucket(ctx, bucket) return newObjectLayerFn().DeleteBucket(ctx, bucket)
+3 -3
View File
@@ -199,7 +199,7 @@ func TestDiskCache(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = cache.Put(ctx, bucketName, objectName, hashReader, httpMeta, opts) err = cache.Put(ctx, bucketName, objectName, NewPutObjReader(hashReader, nil, nil), httpMeta, opts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -275,12 +275,12 @@ func TestDiskCacheMaxUse(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if !cache.diskAvailable(int64(size)) { if !cache.diskAvailable(int64(size)) {
err = cache.Put(ctx, bucketName, objectName, hashReader, httpMeta, opts) err = cache.Put(ctx, bucketName, objectName, NewPutObjReader(hashReader, nil, nil), httpMeta, opts)
if err != errDiskFull { if err != errDiskFull {
t.Fatal("Cache max-use limit violated.") t.Fatal("Cache max-use limit violated.")
} }
} else { } else {
err = cache.Put(ctx, bucketName, objectName, hashReader, httpMeta, opts) err = cache.Put(ctx, bucketName, objectName, NewPutObjReader(hashReader, nil, nil), httpMeta, opts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
+6 -4
View File
@@ -21,7 +21,6 @@ import (
"io" "io"
"net/http" "net/http"
"github.com/minio/minio/pkg/hash"
"github.com/minio/minio/pkg/madmin" "github.com/minio/minio/pkg/madmin"
"github.com/minio/minio/pkg/policy" "github.com/minio/minio/pkg/policy"
) )
@@ -72,7 +71,7 @@ func (api *DummyObjectLayer) GetObjectInfo(ctx context.Context, bucket, object s
return return
} }
func (api *DummyObjectLayer) PutObject(ctx context.Context, bucket, object string, data *hash.Reader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, err error) { func (api *DummyObjectLayer) PutObject(ctx context.Context, bucket, object string, data *PutObjReader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, err error) {
return return
} }
@@ -96,7 +95,7 @@ func (api *DummyObjectLayer) CopyObjectPart(ctx context.Context, srcBucket, srcO
return return
} }
func (api *DummyObjectLayer) PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, data *hash.Reader, opts ObjectOptions) (info PartInfo, err error) { func (api *DummyObjectLayer) PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, data *PutObjReader, opts ObjectOptions) (info PartInfo, err error) {
return return
} }
@@ -108,7 +107,7 @@ func (api *DummyObjectLayer) AbortMultipartUpload(ctx context.Context, bucket, o
return return
} }
func (api *DummyObjectLayer) CompleteMultipartUpload(ctx context.Context, bucket, object, uploadID string, uploadedParts []CompletePart) (objInfo ObjectInfo, err error) { func (api *DummyObjectLayer) CompleteMultipartUpload(ctx context.Context, bucket, object, uploadID string, uploadedParts []CompletePart, opts ObjectOptions) (objInfo ObjectInfo, err error) {
return return
} }
@@ -155,6 +154,9 @@ func (api *DummyObjectLayer) DeleteBucketPolicy(context.Context, string) (err er
func (api *DummyObjectLayer) IsNotificationSupported() (b bool) { func (api *DummyObjectLayer) IsNotificationSupported() (b bool) {
return return
} }
func (api *DummyObjectLayer) IsListenBucketSupported() (b bool) {
return
}
func (api *DummyObjectLayer) IsEncryptionSupported() (b bool) { func (api *DummyObjectLayer) IsEncryptionSupported() (b bool) {
return return
+72 -12
View File
@@ -22,6 +22,7 @@ import (
"crypto/rand" "crypto/rand"
"crypto/subtle" "crypto/subtle"
"encoding/binary" "encoding/binary"
"encoding/hex"
"errors" "errors"
"io" "io"
"net/http" "net/http"
@@ -205,21 +206,20 @@ func newEncryptMetadata(key []byte, bucket, object string, metadata map[string]s
sealedKey = objectKey.Seal(extKey, crypto.GenerateIV(rand.Reader), crypto.SSEC.String(), bucket, object) sealedKey = objectKey.Seal(extKey, crypto.GenerateIV(rand.Reader), crypto.SSEC.String(), bucket, object)
crypto.SSEC.CreateMetadata(metadata, sealedKey) crypto.SSEC.CreateMetadata(metadata, sealedKey)
return objectKey[:], nil return objectKey[:], nil
} }
func newEncryptReader(content io.Reader, key []byte, bucket, object string, metadata map[string]string, sseS3 bool) (io.Reader, error) { func newEncryptReader(content io.Reader, key []byte, bucket, object string, metadata map[string]string, sseS3 bool) (r io.Reader, encKey []byte, err error) {
objectEncryptionKey, err := newEncryptMetadata(key, bucket, object, metadata, sseS3) objectEncryptionKey, err := newEncryptMetadata(key, bucket, object, metadata, sseS3)
if err != nil { if err != nil {
return nil, err return nil, encKey, err
} }
reader, err := sio.EncryptReader(content, sio.Config{Key: objectEncryptionKey[:], MinVersion: sio.Version20}) reader, err := sio.EncryptReader(content, sio.Config{Key: objectEncryptionKey[:], MinVersion: sio.Version20})
if err != nil { if err != nil {
return nil, crypto.ErrInvalidCustomerKey return nil, encKey, crypto.ErrInvalidCustomerKey
} }
return reader, nil return reader, objectEncryptionKey, nil
} }
// set new encryption metadata from http request headers for SSE-C and generated key from KMS in the case of // set new encryption metadata from http request headers for SSE-C and generated key from KMS in the case of
@@ -241,19 +241,18 @@ func setEncryptionMetadata(r *http.Request, bucket, object string, metadata map[
// EncryptRequest takes the client provided content and encrypts the data // EncryptRequest takes the client provided content and encrypts the data
// with the client provided key. It also marks the object as client-side-encrypted // with the client provided key. It also marks the object as client-side-encrypted
// and sets the correct headers. // and sets the correct headers.
func EncryptRequest(content io.Reader, r *http.Request, bucket, object string, metadata map[string]string) (io.Reader, error) { func EncryptRequest(content io.Reader, r *http.Request, bucket, object string, metadata map[string]string) (reader io.Reader, objEncKey []byte, err error) {
var ( var (
key []byte key []byte
err error
) )
if crypto.S3.IsRequested(r.Header) && crypto.SSEC.IsRequested(r.Header) { if crypto.S3.IsRequested(r.Header) && crypto.SSEC.IsRequested(r.Header) {
return nil, crypto.ErrIncompatibleEncryptionMethod return nil, objEncKey, crypto.ErrIncompatibleEncryptionMethod
} }
if crypto.SSEC.IsRequested(r.Header) { if crypto.SSEC.IsRequested(r.Header) {
key, err = ParseSSECustomerRequest(r) key, err = ParseSSECustomerRequest(r)
if err != nil { if err != nil {
return nil, err return nil, objEncKey, err
} }
} }
return newEncryptReader(content, key, bucket, object, metadata, crypto.S3.IsRequested(r.Header)) return newEncryptReader(content, key, bucket, object, metadata, crypto.S3.IsRequested(r.Header))
@@ -578,7 +577,6 @@ func (d *DecryptBlocksReader) Read(p []byte) (int, error) {
d.partDecRelOffset += int64(n1) d.partDecRelOffset += int64(n1)
} }
return len(p), nil return len(p), nil
} }
@@ -813,7 +811,6 @@ func DecryptBlocksRequest(client io.Writer, r *http.Request, bucket, object stri
// getEncryptedMultipartsOffsetLength - fetch sequence number, encrypted start offset and encrypted length. // getEncryptedMultipartsOffsetLength - fetch sequence number, encrypted start offset and encrypted length.
func getEncryptedMultipartsOffsetLength(offset, length int64, obj ObjectInfo) (uint32, int64, int64) { func getEncryptedMultipartsOffsetLength(offset, length int64, obj ObjectInfo) (uint32, int64, int64) {
// Calculate encrypted offset of a multipart object // Calculate encrypted offset of a multipart object
computeEncOffset := func(off int64, obj ObjectInfo) (seqNumber uint32, encryptedOffset int64, err error) { computeEncOffset := func(off int64, obj ObjectInfo) (seqNumber uint32, encryptedOffset int64, err error) {
var curPartEndOffset uint64 var curPartEndOffset uint64
@@ -909,6 +906,64 @@ func (o *ObjectInfo) DecryptedSize() (int64, error) {
return size, nil return size, nil
} }
// For encrypted objects, the ETag sent by client if available
// is stored in encrypted form in the backend. Decrypt the ETag
// if ETag was previously encrypted.
func getDecryptedETag(headers http.Header, objInfo ObjectInfo, copySource bool) (decryptedETag string) {
var (
key [32]byte
err error
)
// If ETag is contentMD5Sum return it as is.
if len(objInfo.ETag) == 32 {
return objInfo.ETag
}
if crypto.IsMultiPart(objInfo.UserDefined) {
return objInfo.ETag
}
if crypto.SSECopy.IsRequested(headers) {
key, err = crypto.SSECopy.ParseHTTP(headers)
if err != nil {
return objInfo.ETag
}
}
// As per AWS S3 Spec, ETag for SSE-C encrypted objects need not be MD5Sum of the data.
// Since server side copy with same source and dest just replaces the ETag, we save
// encrypted content MD5Sum as ETag for both SSE-C and SSE-S3, we standardize the ETag
//encryption across SSE-C and SSE-S3, and only return last 32 bytes for SSE-C
if crypto.SSEC.IsEncrypted(objInfo.UserDefined) && !copySource {
return objInfo.ETag[len(objInfo.ETag)-32:]
}
objectEncryptionKey, err := decryptObjectInfo(key[:], objInfo.Bucket, objInfo.Name, objInfo.UserDefined)
if err != nil {
return objInfo.ETag
}
return tryDecryptETag(objectEncryptionKey, objInfo.ETag, false)
}
// helper to decrypt Etag given object encryption key and encrypted ETag
func tryDecryptETag(key []byte, encryptedETag string, ssec bool) string {
// ETag for SSE-C encrypted objects need not be content MD5Sum.While encrypted
// md5sum is stored internally, return just the last 32 bytes of hex-encoded and
// encrypted md5sum string for SSE-C
if ssec {
return encryptedETag[len(encryptedETag)-32:]
}
var objectKey crypto.ObjectKey
copy(objectKey[:], key)
encBytes, err := hex.DecodeString(encryptedETag)
if err != nil {
return encryptedETag
}
etagBytes, err := objectKey.UnsealETag(encBytes)
if err != nil {
return encryptedETag
}
return hex.EncodeToString(etagBytes)
}
// GetDecryptedRange - To decrypt the range (off, length) of the // GetDecryptedRange - To decrypt the range (off, length) of the
// decrypted object stream, we need to read the range (encOff, // decrypted object stream, we need to read the range (encOff,
// encLength) of the encrypted object stream to decrypt it, and // encLength) of the encrypted object stream to decrypt it, and
@@ -1075,7 +1130,7 @@ func DecryptCopyObjectInfo(info *ObjectInfo, headers http.Header) (apiErr APIErr
// decryption succeeded. // decryption succeeded.
// //
// DecryptObjectInfo also returns whether the object is encrypted or not. // DecryptObjectInfo also returns whether the object is encrypted or not.
func DecryptObjectInfo(info ObjectInfo, headers http.Header) (encrypted bool, err error) { func DecryptObjectInfo(info *ObjectInfo, headers http.Header) (encrypted bool, err error) {
// Directories are never encrypted. // Directories are never encrypted.
if info.IsDir { if info.IsDir {
return false, nil return false, nil
@@ -1094,6 +1149,11 @@ func DecryptObjectInfo(info ObjectInfo, headers http.Header) (encrypted bool, er
return return
} }
_, err = info.DecryptedSize() _, err = info.DecryptedSize()
if crypto.IsEncrypted(info.UserDefined) && !crypto.IsMultiPart(info.UserDefined) {
info.ETag = getDecryptedETag(headers, *info, false)
}
} }
return return
} }
+2 -2
View File
@@ -139,7 +139,7 @@ func TestEncryptRequest(t *testing.T) {
for k, v := range test.header { for k, v := range test.header {
req.Header.Set(k, v) req.Header.Set(k, v)
} }
_, err := EncryptRequest(content, req, "bucket", "object", test.metadata) _, _, err := EncryptRequest(content, req, "bucket", "object", test.metadata)
if err != nil { if err != nil {
t.Fatalf("Test %d: Failed to encrypt request: %v", i, err) t.Fatalf("Test %d: Failed to encrypt request: %v", i, err)
@@ -324,7 +324,7 @@ var decryptObjectInfoTests = []struct {
func TestDecryptObjectInfo(t *testing.T) { func TestDecryptObjectInfo(t *testing.T) {
for i, test := range decryptObjectInfoTests { for i, test := range decryptObjectInfoTests {
if encrypted, err := DecryptObjectInfo(test.info, test.headers); err != test.expErr { if encrypted, err := DecryptObjectInfo(&test.info, test.headers); err != test.expErr {
t.Errorf("Test %d: Decryption returned wrong error code: got %d , want %d", i, err, test.expErr) t.Errorf("Test %d: Decryption returned wrong error code: got %d , want %d", i, err, test.expErr)
} else if enc := crypto.IsEncrypted(test.info.UserDefined); encrypted && enc != encrypted { } else if enc := crypto.IsEncrypted(test.info.UserDefined); encrypted && enc != encrypted {
t.Errorf("Test %d: Decryption thinks object is encrypted but it is not", i) t.Errorf("Test %d: Decryption thinks object is encrypted but it is not", i)
+20 -15
View File
@@ -455,7 +455,12 @@ func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList,
return serverAddr, endpoints, setupType, err return serverAddr, endpoints, setupType, err
} }
updateDomainIPs(uniqueArgs) _, dok := os.LookupEnv("MINIO_DOMAIN")
_, eok := os.LookupEnv("MINIO_ETCD_ENDPOINTS")
_, iok := os.LookupEnv("MINIO_PUBLIC_IPS")
if dok && eok && !iok {
updateDomainIPs(uniqueArgs)
}
setupType = DistXLSetupType setupType = DistXLSetupType
return serverAddr, endpoints, setupType, nil return serverAddr, endpoints, setupType, nil
@@ -509,21 +514,21 @@ func GetRemotePeers(endpoints EndpointList) []string {
return peerSet.ToSlice() return peerSet.ToSlice()
} }
// In federated and distributed setup, update IP addresses of the hosts passed in command line
// if MINIO_PUBLIC_IPS are not set manually
func updateDomainIPs(endPoints set.StringSet) { func updateDomainIPs(endPoints set.StringSet) {
_, dok := os.LookupEnv("MINIO_DOMAIN") ipList := set.NewStringSet()
_, eok := os.LookupEnv("MINIO_ETCD_ENDPOINTS") for e := range endPoints {
_, iok := os.LookupEnv("MINIO_PUBLIC_IPS") host, _, err := net.SplitHostPort(e)
if dok && eok && !iok { if err != nil {
globalDomainIPs = set.NewStringSet() if strings.Contains(err.Error(), "missing port in address") {
for e := range endPoints { host = e
host, _, _ := net.SplitHostPort(e) } else {
ipList, _ := getHostIP4(host) continue
remoteIPList := ipList.FuncMatch(func(ip string, matchString string) bool { }
return !strings.HasPrefix(ip, "127.")
}, "")
globalDomainIPs.Add(remoteIPList.ToSlice()[0])
} }
IPs, _ := getHostIP4(host)
ipList = ipList.Union(IPs)
} }
globalDomainIPs = ipList.FuncMatch(func(ip string, matchString string) bool {
return !strings.HasPrefix(ip, "127.")
}, "")
} }
+1 -2
View File
@@ -24,7 +24,6 @@ import (
"path" "path"
"time" "time"
"github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/lock" "github.com/minio/minio/pkg/lock"
) )
@@ -273,7 +272,7 @@ func initFormatFS(ctx context.Context, fsPath string) (rlk *lock.RLockedFile, er
rlk.Close() rlk.Close()
return nil, err return nil, err
} }
logger.SetDeploymentID(id) globalDeploymentID = id
return rlk, nil return rlk, nil
} }
} }
+1
View File
@@ -381,6 +381,7 @@ func saveFormatXL(disk StorageAPI, format interface{}) error {
var ignoredHiddenDirectories = []string{ var ignoredHiddenDirectories = []string{
minioMetaBucket, minioMetaBucket,
".snapshot",
"lost+found", "lost+found",
"$RECYCLE.BIN", "$RECYCLE.BIN",
"System Volume Information", "System Volume Information",
+2 -2
View File
@@ -54,7 +54,7 @@ func TestReadFSMetadata(t *testing.T) {
if err := obj.MakeBucketWithLocation(context.Background(), bucketName, ""); err != nil { if err := obj.MakeBucketWithLocation(context.Background(), bucketName, ""); err != nil {
t.Fatal("Unexpected err: ", err) t.Fatal("Unexpected err: ", err)
} }
if _, err := obj.PutObject(context.Background(), bucketName, objectName, mustGetHashReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil, ObjectOptions{}); err != nil { if _, err := obj.PutObject(context.Background(), bucketName, objectName, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil, ObjectOptions{}); err != nil {
t.Fatal("Unexpected err: ", err) t.Fatal("Unexpected err: ", err)
} }
@@ -89,7 +89,7 @@ func TestWriteFSMetadata(t *testing.T) {
if err := obj.MakeBucketWithLocation(context.Background(), bucketName, ""); err != nil { if err := obj.MakeBucketWithLocation(context.Background(), bucketName, ""); err != nil {
t.Fatal("Unexpected err: ", err) t.Fatal("Unexpected err: ", err)
} }
if _, err := obj.PutObject(context.Background(), bucketName, objectName, mustGetHashReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil, ObjectOptions{}); err != nil { if _, err := obj.PutObject(context.Background(), bucketName, objectName, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil, ObjectOptions{}); err != nil {
t.Fatal("Unexpected err: ", err) t.Fatal("Unexpected err: ", err)
} }
+6 -7
View File
@@ -18,7 +18,6 @@ package cmd
import ( import (
"context" "context"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@@ -31,8 +30,6 @@ import (
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
mioutil "github.com/minio/minio/pkg/ioutil" mioutil "github.com/minio/minio/pkg/ioutil"
"github.com/minio/minio/pkg/hash"
) )
// Returns EXPORT/.minio.sys/multipart/SHA256/UPLOADID // Returns EXPORT/.minio.sys/multipart/SHA256/UPLOADID
@@ -259,7 +256,7 @@ func (fs *FSObjects) CopyObjectPart(ctx context.Context, srcBucket, srcObject, d
return pi, toObjectErr(err) return pi, toObjectErr(err)
} }
partInfo, err := fs.PutObjectPart(ctx, dstBucket, dstObject, uploadID, partID, srcInfo.Reader, dstOpts) partInfo, err := fs.PutObjectPart(ctx, dstBucket, dstObject, uploadID, partID, srcInfo.PutObjReader, dstOpts)
if err != nil { if err != nil {
return pi, toObjectErr(err, dstBucket, dstObject) return pi, toObjectErr(err, dstBucket, dstObject)
} }
@@ -271,7 +268,8 @@ func (fs *FSObjects) CopyObjectPart(ctx context.Context, srcBucket, srcObject, d
// an ongoing multipart transaction. Internally incoming data is // an ongoing multipart transaction. Internally incoming data is
// written to '.minio.sys/tmp' location and safely renamed to // written to '.minio.sys/tmp' location and safely renamed to
// '.minio.sys/multipart' for reach parts. // '.minio.sys/multipart' for reach parts.
func (fs *FSObjects) PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, data *hash.Reader, opts ObjectOptions) (pi PartInfo, e error) { func (fs *FSObjects) PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, r *PutObjReader, opts ObjectOptions) (pi PartInfo, e error) {
data := r.Reader
if err := checkPutObjectPartArgs(ctx, bucket, object, fs); err != nil { if err := checkPutObjectPartArgs(ctx, bucket, object, fs); err != nil {
return pi, toObjectErr(err, bucket) return pi, toObjectErr(err, bucket)
} }
@@ -321,11 +319,12 @@ func (fs *FSObjects) PutObjectPart(ctx context.Context, bucket, object, uploadID
// PutObjectPart succeeds then there would be nothing to // PutObjectPart succeeds then there would be nothing to
// delete in which case we just ignore the error. // delete in which case we just ignore the error.
defer fsRemoveFile(ctx, tmpPartPath) defer fsRemoveFile(ctx, tmpPartPath)
etag := r.MD5CurrentHexString()
etag := hex.EncodeToString(data.MD5Current())
if etag == "" { if etag == "" {
etag = GenETag() etag = GenETag()
} }
partPath := pathJoin(uploadIDDir, fs.encodePartFile(partID, etag, data.ActualSize())) partPath := pathJoin(uploadIDDir, fs.encodePartFile(partID, etag, data.ActualSize()))
if err = fsRenameFile(ctx, tmpPartPath, partPath); err != nil { if err = fsRenameFile(ctx, tmpPartPath, partPath); err != nil {
@@ -478,7 +477,7 @@ func (fs *FSObjects) ListObjectParts(ctx context.Context, bucket, object, upload
// md5sums of all the parts. // md5sums of all the parts.
// //
// Implements S3 compatible Complete multipart API. // Implements S3 compatible Complete multipart API.
func (fs *FSObjects) CompleteMultipartUpload(ctx context.Context, bucket string, object string, uploadID string, parts []CompletePart) (oi ObjectInfo, e error) { func (fs *FSObjects) CompleteMultipartUpload(ctx context.Context, bucket string, object string, uploadID string, parts []CompletePart, opts ObjectOptions) (oi ObjectInfo, e error) {
var actualSize int64 var actualSize int64
+5 -5
View File
@@ -114,7 +114,7 @@ func TestPutObjectPartFaultyDisk(t *testing.T) {
sha256sum := "" sha256sum := ""
fs.fsPath = filepath.Join(globalTestTmpDir, "minio-"+nextSuffix()) fs.fsPath = filepath.Join(globalTestTmpDir, "minio-"+nextSuffix())
_, err = fs.PutObjectPart(context.Background(), bucketName, objectName, uploadID, 1, mustGetHashReader(t, bytes.NewReader(data), dataLen, md5Hex, sha256sum), ObjectOptions{}) _, err = fs.PutObjectPart(context.Background(), bucketName, objectName, uploadID, 1, mustGetPutObjReader(t, bytes.NewReader(data), dataLen, md5Hex, sha256sum), ObjectOptions{})
if !isSameType(err, BucketNotFound{}) { if !isSameType(err, BucketNotFound{}) {
t.Fatal("Unexpected error ", err) t.Fatal("Unexpected error ", err)
} }
@@ -145,7 +145,7 @@ func TestCompleteMultipartUploadFaultyDisk(t *testing.T) {
parts := []CompletePart{{PartNumber: 1, ETag: md5Hex}} parts := []CompletePart{{PartNumber: 1, ETag: md5Hex}}
fs.fsPath = filepath.Join(globalTestTmpDir, "minio-"+nextSuffix()) fs.fsPath = filepath.Join(globalTestTmpDir, "minio-"+nextSuffix())
if _, err := fs.CompleteMultipartUpload(context.Background(), bucketName, objectName, uploadID, parts); err != nil { if _, err := fs.CompleteMultipartUpload(context.Background(), bucketName, objectName, uploadID, parts, ObjectOptions{}); err != nil {
if !isSameType(err, BucketNotFound{}) { if !isSameType(err, BucketNotFound{}) {
t.Fatal("Unexpected error ", err) t.Fatal("Unexpected error ", err)
} }
@@ -175,13 +175,13 @@ func TestCompleteMultipartUpload(t *testing.T) {
md5Hex := getMD5Hash(data) md5Hex := getMD5Hash(data)
if _, err := fs.PutObjectPart(context.Background(), bucketName, objectName, uploadID, 1, mustGetHashReader(t, bytes.NewReader(data), 5, md5Hex, ""), ObjectOptions{}); err != nil { if _, err := fs.PutObjectPart(context.Background(), bucketName, objectName, uploadID, 1, mustGetPutObjReader(t, bytes.NewReader(data), 5, md5Hex, ""), ObjectOptions{}); err != nil {
t.Fatal("Unexpected error ", err) t.Fatal("Unexpected error ", err)
} }
parts := []CompletePart{{PartNumber: 1, ETag: md5Hex}} parts := []CompletePart{{PartNumber: 1, ETag: md5Hex}}
if _, err := fs.CompleteMultipartUpload(context.Background(), bucketName, objectName, uploadID, parts); err != nil { if _, err := fs.CompleteMultipartUpload(context.Background(), bucketName, objectName, uploadID, parts, ObjectOptions{}); err != nil {
t.Fatal("Unexpected error ", err) t.Fatal("Unexpected error ", err)
} }
} }
@@ -209,7 +209,7 @@ func TestAbortMultipartUpload(t *testing.T) {
md5Hex := getMD5Hash(data) md5Hex := getMD5Hash(data)
if _, err := fs.PutObjectPart(context.Background(), bucketName, objectName, uploadID, 1, mustGetHashReader(t, bytes.NewReader(data), 5, md5Hex, ""), ObjectOptions{}); err != nil { if _, err := fs.PutObjectPart(context.Background(), bucketName, objectName, uploadID, 1, mustGetPutObjReader(t, bytes.NewReader(data), 5, md5Hex, ""), ObjectOptions{}); err != nil {
t.Fatal("Unexpected error ", err) t.Fatal("Unexpected error ", err)
} }
time.Sleep(time.Second) // Without Sleep on windows, the fs.AbortMultipartUpload() fails with "The process cannot access the file because it is being used by another process." time.Sleep(time.Second) // Without Sleep on windows, the fs.AbortMultipartUpload() fails with "The process cannot access the file because it is being used by another process."
+17 -24
View File
@@ -19,7 +19,6 @@ package cmd
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/hex"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@@ -32,7 +31,6 @@ import (
"time" "time"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/hash"
"github.com/minio/minio/pkg/lock" "github.com/minio/minio/pkg/lock"
"github.com/minio/minio/pkg/madmin" "github.com/minio/minio/pkg/madmin"
"github.com/minio/minio/pkg/mimedb" "github.com/minio/minio/pkg/mimedb"
@@ -189,14 +187,9 @@ func (fs *FSObjects) diskUsage(doneCh chan struct{}) {
case <-doneCh: case <-doneCh:
return errWalkAbort return errWalkAbort
default: default:
var fi os.FileInfo fi, err := os.Stat(entry)
var err error
if hasSuffix(entry, slashSeparator) {
fi, err = fsStatDir(ctx, entry)
} else {
fi, err = fsStatFile(ctx, entry)
}
if err != nil { if err != nil {
err = osErrToFSFileErr(err)
return err return err
} }
atomic.AddUint64(&fs.totalUsed, uint64(fi.Size())) atomic.AddUint64(&fs.totalUsed, uint64(fi.Size()))
@@ -228,14 +221,9 @@ func (fs *FSObjects) diskUsage(doneCh chan struct{}) {
} }
} }
var fi os.FileInfo fi, err := os.Stat(entry)
var err error
if hasSuffix(entry, slashSeparator) {
fi, err = fsStatDir(ctx, entry)
} else {
fi, err = fsStatFile(ctx, entry)
}
if err != nil { if err != nil {
err = osErrToFSFileErr(err)
return err return err
} }
usage = usage + uint64(fi.Size()) usage = usage + uint64(fi.Size())
@@ -461,8 +449,7 @@ func (fs *FSObjects) CopyObject(ctx context.Context, srcBucket, srcObject, dstBu
// Return the new object info. // Return the new object info.
return fsMeta.ToObjectInfo(srcBucket, srcObject, fi), nil return fsMeta.ToObjectInfo(srcBucket, srcObject, fi), nil
} }
objInfo, err := fs.putObject(ctx, dstBucket, dstObject, srcInfo.PutObjReader, srcInfo.UserDefined, dstOpts)
objInfo, err := fs.putObject(ctx, dstBucket, dstObject, srcInfo.Reader, srcInfo.UserDefined)
if err != nil { if err != nil {
return oi, toObjectErr(err, dstBucket, dstObject) return oi, toObjectErr(err, dstBucket, dstObject)
} }
@@ -817,8 +804,8 @@ func (fs *FSObjects) parentDirIsObject(ctx context.Context, bucket, parent strin
// until EOF, writes data directly to configured filesystem path. // until EOF, writes data directly to configured filesystem path.
// Additionally writes `fs.json` which carries the necessary metadata // Additionally writes `fs.json` which carries the necessary metadata
// for future object operations. // for future object operations.
func (fs *FSObjects) PutObject(ctx context.Context, bucket string, object string, data *hash.Reader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, retErr error) { func (fs *FSObjects) PutObject(ctx context.Context, bucket string, object string, r *PutObjReader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, retErr error) {
if err := checkPutObjectArgs(ctx, bucket, object, fs, data.Size()); err != nil { if err := checkPutObjectArgs(ctx, bucket, object, fs, r.Size()); err != nil {
return ObjectInfo{}, err return ObjectInfo{}, err
} }
// Lock the object. // Lock the object.
@@ -828,11 +815,13 @@ func (fs *FSObjects) PutObject(ctx context.Context, bucket string, object string
return objInfo, err return objInfo, err
} }
defer objectLock.Unlock() defer objectLock.Unlock()
return fs.putObject(ctx, bucket, object, data, metadata) return fs.putObject(ctx, bucket, object, r, metadata, opts)
} }
// putObject - wrapper for PutObject // putObject - wrapper for PutObject
func (fs *FSObjects) putObject(ctx context.Context, bucket string, object string, data *hash.Reader, metadata map[string]string) (objInfo ObjectInfo, retErr error) { func (fs *FSObjects) putObject(ctx context.Context, bucket string, object string, r *PutObjReader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, retErr error) {
data := r.Reader
// No metadata is set, allocate a new one. // No metadata is set, allocate a new one.
meta := make(map[string]string) meta := make(map[string]string)
for k, v := range metadata { for k, v := range metadata {
@@ -923,8 +912,7 @@ func (fs *FSObjects) putObject(ctx context.Context, bucket string, object string
fsRemoveFile(ctx, fsTmpObjPath) fsRemoveFile(ctx, fsTmpObjPath)
return ObjectInfo{}, toObjectErr(err, bucket, object) return ObjectInfo{}, toObjectErr(err, bucket, object)
} }
fsMeta.Meta["etag"] = r.MD5CurrentHexString()
fsMeta.Meta["etag"] = hex.EncodeToString(data.MD5Current())
// Should return IncompleteBody{} error when reader has fewer // Should return IncompleteBody{} error when reader has fewer
// bytes than specified in request header. // bytes than specified in request header.
@@ -1318,6 +1306,11 @@ func (fs *FSObjects) IsNotificationSupported() bool {
return true return true
} }
// IsListenBucketSupported returns whether listen bucket notification is applicable for this layer.
func (fs *FSObjects) IsListenBucketSupported() bool {
return true
}
// IsEncryptionSupported returns whether server side encryption is applicable for this layer. // IsEncryptionSupported returns whether server side encryption is applicable for this layer.
func (fs *FSObjects) IsEncryptionSupported() bool { func (fs *FSObjects) IsEncryptionSupported() bool {
return true return true
+8 -8
View File
@@ -40,7 +40,7 @@ func TestFSParentDirIsObject(t *testing.T) {
} }
objectContent := "12345" objectContent := "12345"
objInfo, err := obj.PutObject(context.Background(), bucketName, objectName, objInfo, err := obj.PutObject(context.Background(), bucketName, objectName,
mustGetHashReader(t, bytes.NewReader([]byte(objectContent)), int64(len(objectContent)), "", ""), nil, ObjectOptions{}) mustGetPutObjReader(t, bytes.NewReader([]byte(objectContent)), int64(len(objectContent)), "", ""), nil, ObjectOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -124,7 +124,7 @@ func TestFSShutdown(t *testing.T) {
objectContent := "12345" objectContent := "12345"
obj.MakeBucketWithLocation(context.Background(), bucketName, "") obj.MakeBucketWithLocation(context.Background(), bucketName, "")
obj.PutObject(context.Background(), bucketName, objectName, mustGetHashReader(t, bytes.NewReader([]byte(objectContent)), int64(len(objectContent)), "", ""), nil, ObjectOptions{}) obj.PutObject(context.Background(), bucketName, objectName, mustGetPutObjReader(t, bytes.NewReader([]byte(objectContent)), int64(len(objectContent)), "", ""), nil, ObjectOptions{})
return fs, disk return fs, disk
} }
@@ -203,7 +203,7 @@ func TestFSPutObject(t *testing.T) {
} }
// With a regular object. // With a regular object.
_, err := obj.PutObject(context.Background(), bucketName+"non-existent", objectName, mustGetHashReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil, ObjectOptions{}) _, err := obj.PutObject(context.Background(), bucketName+"non-existent", objectName, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil, ObjectOptions{})
if err == nil { if err == nil {
t.Fatal("Unexpected should fail here, bucket doesn't exist") t.Fatal("Unexpected should fail here, bucket doesn't exist")
} }
@@ -212,7 +212,7 @@ func TestFSPutObject(t *testing.T) {
} }
// With a directory object. // With a directory object.
_, err = obj.PutObject(context.Background(), bucketName+"non-existent", objectName+"/", mustGetHashReader(t, bytes.NewReader([]byte("abcd")), 0, "", ""), nil, ObjectOptions{}) _, err = obj.PutObject(context.Background(), bucketName+"non-existent", objectName+"/", mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), 0, "", ""), nil, ObjectOptions{})
if err == nil { if err == nil {
t.Fatal("Unexpected should fail here, bucket doesn't exist") t.Fatal("Unexpected should fail here, bucket doesn't exist")
} }
@@ -220,11 +220,11 @@ func TestFSPutObject(t *testing.T) {
t.Fatalf("Expected error type BucketNotFound, got %#v", err) t.Fatalf("Expected error type BucketNotFound, got %#v", err)
} }
_, err = obj.PutObject(context.Background(), bucketName, objectName, mustGetHashReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil, ObjectOptions{}) _, err = obj.PutObject(context.Background(), bucketName, objectName, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil, ObjectOptions{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, err = obj.PutObject(context.Background(), bucketName, objectName+"/1", mustGetHashReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil, ObjectOptions{}) _, err = obj.PutObject(context.Background(), bucketName, objectName+"/1", mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil, ObjectOptions{})
if err == nil { if err == nil {
t.Fatal("Unexpected should fail here, backend corruption occurred") t.Fatal("Unexpected should fail here, backend corruption occurred")
} }
@@ -239,7 +239,7 @@ func TestFSPutObject(t *testing.T) {
} }
} }
_, err = obj.PutObject(context.Background(), bucketName, objectName+"/1/", mustGetHashReader(t, bytes.NewReader([]byte("abcd")), 0, "", ""), nil, ObjectOptions{}) _, err = obj.PutObject(context.Background(), bucketName, objectName+"/1/", mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), 0, "", ""), nil, ObjectOptions{})
if err == nil { if err == nil {
t.Fatal("Unexpected should fail here, backned corruption occurred") t.Fatal("Unexpected should fail here, backned corruption occurred")
} }
@@ -267,7 +267,7 @@ func TestFSDeleteObject(t *testing.T) {
objectName := "object" objectName := "object"
obj.MakeBucketWithLocation(context.Background(), bucketName, "") obj.MakeBucketWithLocation(context.Background(), bucketName, "")
obj.PutObject(context.Background(), bucketName, objectName, mustGetHashReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil, ObjectOptions{}) obj.PutObject(context.Background(), bucketName, objectName, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil, ObjectOptions{})
// Test with invalid bucket name // Test with invalid bucket name
if err := fs.DeleteObject(context.Background(), "fo", objectName); !isSameType(err, BucketNameInvalid{}) { if err := fs.DeleteObject(context.Background(), "fo", objectName); !isSameType(err, BucketNameInvalid{}) {
+33 -17
View File
@@ -41,7 +41,7 @@ func init() {
var ( var (
gatewayCmd = cli.Command{ gatewayCmd = cli.Command{
Name: "gateway", Name: "gateway",
Usage: "Start object storage gateway.", Usage: "start object storage gateway",
Flags: append(serverFlags, globalFlags...), Flags: append(serverFlags, globalFlags...),
HideHelpCommand: true, HideHelpCommand: true,
} }
@@ -135,7 +135,8 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
handleCommonCmdArgs(ctx) handleCommonCmdArgs(ctx)
// Get port to listen on from gateway address // Get port to listen on from gateway address
_, gatewayPort, pErr := net.SplitHostPort(gatewayAddr) var pErr error
_, globalMinioPort, pErr = net.SplitHostPort(gatewayAddr)
if pErr != nil { if pErr != nil {
logger.FatalIf(pErr, "Unable to start gateway") logger.FatalIf(pErr, "Unable to start gateway")
} }
@@ -144,7 +145,7 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
// to IPv6 address ie minio will start listening on IPv6 address whereas another // to IPv6 address ie minio will start listening on IPv6 address whereas another
// (non-)minio process is listening on IPv4 of given port. // (non-)minio process is listening on IPv4 of given port.
// To avoid this error situation we check for port availability. // To avoid this error situation we check for port availability.
logger.FatalIf(checkPortAvailability(gatewayPort), "Unable to start the gateway") logger.FatalIf(checkPortAvailability(globalMinioPort), "Unable to start the gateway")
// Create certs path. // Create certs path.
logger.FatalIf(createConfigDir(), "Unable to create configuration directories") logger.FatalIf(createConfigDir(), "Unable to create configuration directories")
@@ -195,6 +196,15 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
// Add API router. // Add API router.
registerAPIRouter(router) registerAPIRouter(router)
// Dummy endpoint representing gateway instance.
globalEndpoints = []Endpoint{{
URL: &url.URL{Path: "/minio/gateway"},
IsLocal: true,
}}
// Initialize Admin Peers.
initGlobalAdminPeers(globalEndpoints)
var getCert certs.GetCertificateFunc var getCert certs.GetCertificateFunc
if globalTLSCerts != nil { if globalTLSCerts != nil {
getCert = globalTLSCerts.GetCertificate getCert = globalTLSCerts.GetCertificate
@@ -220,26 +230,30 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
// Create a new config system. // Create a new config system.
globalConfigSys = NewConfigSys() globalConfigSys = NewConfigSys()
if globalEtcdClient != nil && gatewayName == "nas" {
// Initialize server config.
_ = globalConfigSys.Init(newObject)
} else {
// Initialize server config.
srvCfg := newServerConfig()
// Initialize server config. // Override any values from ENVs.
srvCfg := newServerConfig() srvCfg.loadFromEnvs()
// Override any values from ENVs. // Load values to cached global values.
srvCfg.loadFromEnvs() srvCfg.loadToCachedConfigs()
// Load values to cached global values. // hold the mutex lock before a new config is assigned.
srvCfg.loadToCachedConfigs() globalServerConfigMu.Lock()
globalServerConfig = srvCfg
// hold the mutex lock before a new config is assigned. globalServerConfigMu.Unlock()
globalServerConfigMu.Lock() }
globalServerConfig = srvCfg
globalServerConfigMu.Unlock()
// Load logger subsystem // Load logger subsystem
loadLoggers() loadLoggers()
// This is only to uniquely identify each gateway deployments. // This is only to uniquely identify each gateway deployments.
logger.SetDeploymentID(os.Getenv("MINIO_GATEWAY_DEPLOYMENT_ID")) globalDeploymentID = os.Getenv("MINIO_GATEWAY_DEPLOYMENT_ID")
var cacheConfig = globalServerConfig.GetCacheConfig() var cacheConfig = globalServerConfig.GetCacheConfig()
if len(cacheConfig.Drives) > 0 { if len(cacheConfig.Drives) > 0 {
@@ -256,7 +270,7 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
globalIAMSys = NewIAMSys() globalIAMSys = NewIAMSys()
if globalEtcdClient != nil { if globalEtcdClient != nil {
// Initialize IAM sys. // Initialize IAM sys.
go globalIAMSys.Init(newObject) _ = globalIAMSys.Init(newObject)
} }
// Create new policy system. // Create new policy system.
@@ -267,7 +281,9 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
// Create new notification system. // Create new notification system.
globalNotificationSys = NewNotificationSys(globalServerConfig, globalEndpoints) globalNotificationSys = NewNotificationSys(globalServerConfig, globalEndpoints)
if globalEtcdClient != nil && newObject.IsNotificationSupported() {
_ = globalNotificationSys.Init(newObject)
}
// Once endpoints are finalized, initialize the new object api. // Once endpoints are finalized, initialize the new object api.
globalObjLayerMutex.Lock() globalObjLayerMutex.Lock()
globalObjectAPI = newObject globalObjectAPI = newObject
+1
View File
@@ -61,6 +61,7 @@ func printGatewayCommonMsg(apiEndpoints []string) {
logger.StartupMessage(colorBlue("AccessKey: ") + colorBold(fmt.Sprintf("%s ", cred.AccessKey))) logger.StartupMessage(colorBlue("AccessKey: ") + colorBold(fmt.Sprintf("%s ", cred.AccessKey)))
logger.StartupMessage(colorBlue("SecretKey: ") + colorBold(fmt.Sprintf("%s ", cred.SecretKey))) logger.StartupMessage(colorBlue("SecretKey: ") + colorBold(fmt.Sprintf("%s ", cred.SecretKey)))
} }
printEventNotifiers()
if globalIsBrowserEnabled { if globalIsBrowserEnabled {
logger.StartupMessage(colorBlue("\nBrowser Access:")) logger.StartupMessage(colorBlue("\nBrowser Access:"))
+9 -3
View File
@@ -20,7 +20,6 @@ import (
"context" "context"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/hash"
"github.com/minio/minio/pkg/madmin" "github.com/minio/minio/pkg/madmin"
"github.com/minio/minio/pkg/policy" "github.com/minio/minio/pkg/policy"
) )
@@ -44,7 +43,8 @@ func (a GatewayUnsupported) CopyObjectPart(ctx context.Context, srcBucket, srcOb
} }
// PutObjectPart puts a part of object in bucket // PutObjectPart puts a part of object in bucket
func (a GatewayUnsupported) PutObjectPart(ctx context.Context, bucket string, object string, uploadID string, partID int, data *hash.Reader, opts ObjectOptions) (pi PartInfo, err error) { func (a GatewayUnsupported) PutObjectPart(ctx context.Context, bucket string, object string, uploadID string, partID int, data *PutObjReader, opts ObjectOptions) (pi PartInfo, err error) {
logger.LogIf(ctx, NotImplemented{})
return pi, NotImplemented{} return pi, NotImplemented{}
} }
@@ -60,7 +60,8 @@ func (a GatewayUnsupported) AbortMultipartUpload(ctx context.Context, bucket str
} }
// CompleteMultipartUpload completes ongoing multipart upload and finalizes object // CompleteMultipartUpload completes ongoing multipart upload and finalizes object
func (a GatewayUnsupported) CompleteMultipartUpload(ctx context.Context, bucket string, object string, uploadID string, uploadedParts []CompletePart) (oi ObjectInfo, err error) { func (a GatewayUnsupported) CompleteMultipartUpload(ctx context.Context, bucket string, object string, uploadID string, uploadedParts []CompletePart, opts ObjectOptions) (oi ObjectInfo, err error) {
logger.LogIf(ctx, NotImplemented{})
return oi, NotImplemented{} return oi, NotImplemented{}
} }
@@ -131,6 +132,11 @@ func (a GatewayUnsupported) IsNotificationSupported() bool {
return false return false
} }
// IsListenBucketSupported returns whether listen bucket notification is applicable for this layer.
func (a GatewayUnsupported) IsListenBucketSupported() bool {
return false
}
// IsEncryptionSupported returns whether server side encryption is applicable for this layer. // IsEncryptionSupported returns whether server side encryption is applicable for this layer.
func (a GatewayUnsupported) IsEncryptionSupported() bool { func (a GatewayUnsupported) IsEncryptionSupported() bool {
return false return false
+5 -4
View File
@@ -38,7 +38,6 @@ import (
miniogopolicy "github.com/minio/minio-go/pkg/policy" miniogopolicy "github.com/minio/minio-go/pkg/policy"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/auth"
"github.com/minio/minio/pkg/hash"
"github.com/minio/minio/pkg/policy" "github.com/minio/minio/pkg/policy"
"github.com/minio/minio/pkg/policy/condition" "github.com/minio/minio/pkg/policy/condition"
sha256 "github.com/minio/sha256-simd" sha256 "github.com/minio/sha256-simd"
@@ -737,7 +736,8 @@ func (a *azureObjects) GetObjectInfo(ctx context.Context, bucket, object string,
// PutObject - Create a new blob with the incoming data, // PutObject - Create a new blob with the incoming data,
// uses Azure equivalent CreateBlockBlobFromReader. // uses Azure equivalent CreateBlockBlobFromReader.
func (a *azureObjects) PutObject(ctx context.Context, bucket, object string, data *hash.Reader, metadata map[string]string, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) { func (a *azureObjects) PutObject(ctx context.Context, bucket, object string, r *minio.PutObjReader, metadata map[string]string, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) {
data := r.Reader
if data.Size() < azureBlockSize/10 { if data.Size() < azureBlockSize/10 {
blob := a.client.GetContainerReference(bucket).GetBlobReference(object) blob := a.client.GetContainerReference(bucket).GetBlobReference(object)
blob.Metadata, blob.Properties, err = s3MetaToAzureProperties(ctx, metadata) blob.Metadata, blob.Properties, err = s3MetaToAzureProperties(ctx, metadata)
@@ -938,7 +938,8 @@ func (a *azureObjects) NewMultipartUpload(ctx context.Context, bucket, object st
} }
// PutObjectPart - Use Azure equivalent PutBlockWithLength. // PutObjectPart - Use Azure equivalent PutBlockWithLength.
func (a *azureObjects) PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, data *hash.Reader, opts minio.ObjectOptions) (info minio.PartInfo, err error) { func (a *azureObjects) PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, r *minio.PutObjReader, opts minio.ObjectOptions) (info minio.PartInfo, err error) {
data := r.Reader
if err = a.checkUploadIDExists(ctx, bucket, object, uploadID); err != nil { if err = a.checkUploadIDExists(ctx, bucket, object, uploadID); err != nil {
return info, err return info, err
} }
@@ -1077,7 +1078,7 @@ func (a *azureObjects) AbortMultipartUpload(ctx context.Context, bucket, object,
} }
// CompleteMultipartUpload - Use Azure equivalent PutBlockList. // CompleteMultipartUpload - Use Azure equivalent PutBlockList.
func (a *azureObjects) CompleteMultipartUpload(ctx context.Context, bucket, object, uploadID string, uploadedParts []minio.CompletePart) (objInfo minio.ObjectInfo, err error) { func (a *azureObjects) CompleteMultipartUpload(ctx context.Context, bucket, object, uploadID string, uploadedParts []minio.CompletePart, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) {
metadataObject := getAzureMetadataObjectName(object, uploadID) metadataObject := getAzureMetadataObjectName(object, uploadID)
if err = a.checkUploadIDExists(ctx, bucket, object, uploadID); err != nil { if err = a.checkUploadIDExists(ctx, bucket, object, uploadID); err != nil {
return objInfo, err return objInfo, err
+7 -4
View File
@@ -92,7 +92,7 @@ EXAMPLES:
` `
minio.RegisterGatewayCommand(cli.Command{ minio.RegisterGatewayCommand(cli.Command{
Name: b2Backend, Name: b2Backend,
Usage: "Backblaze B2.", Usage: "Backblaze B2",
Action: b2GatewayMain, Action: b2GatewayMain,
CustomHelpTemplate: b2GatewayTemplate, CustomHelpTemplate: b2GatewayTemplate,
HideHelpCommand: true, HideHelpCommand: true,
@@ -534,7 +534,9 @@ func (nb *Reader) Read(p []byte) (int, error) {
} }
// PutObject uploads the single upload to B2 backend by using *b2_upload_file* API, uploads upto 5GiB. // PutObject uploads the single upload to B2 backend by using *b2_upload_file* API, uploads upto 5GiB.
func (l *b2Objects) PutObject(ctx context.Context, bucket string, object string, data *h2.Reader, metadata map[string]string, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) { func (l *b2Objects) PutObject(ctx context.Context, bucket string, object string, r *minio.PutObjReader, metadata map[string]string, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) {
data := r.Reader
bkt, err := l.Bucket(ctx, bucket) bkt, err := l.Bucket(ctx, bucket)
if err != nil { if err != nil {
return objInfo, err return objInfo, err
@@ -653,7 +655,8 @@ func (l *b2Objects) NewMultipartUpload(ctx context.Context, bucket string, objec
} }
// PutObjectPart puts a part of object in bucket, uses B2's LargeFile upload API. // PutObjectPart puts a part of object in bucket, uses B2's LargeFile upload API.
func (l *b2Objects) PutObjectPart(ctx context.Context, bucket string, object string, uploadID string, partID int, data *h2.Reader, opts minio.ObjectOptions) (pi minio.PartInfo, err error) { func (l *b2Objects) PutObjectPart(ctx context.Context, bucket string, object string, uploadID string, partID int, r *minio.PutObjReader, opts minio.ObjectOptions) (pi minio.PartInfo, err error) {
data := r.Reader
bkt, err := l.Bucket(ctx, bucket) bkt, err := l.Bucket(ctx, bucket)
if err != nil { if err != nil {
return pi, err return pi, err
@@ -726,7 +729,7 @@ func (l *b2Objects) AbortMultipartUpload(ctx context.Context, bucket string, obj
} }
// CompleteMultipartUpload completes ongoing multipart upload and finalizes object, uses B2's LargeFile upload API. // CompleteMultipartUpload completes ongoing multipart upload and finalizes object, uses B2's LargeFile upload API.
func (l *b2Objects) CompleteMultipartUpload(ctx context.Context, bucket string, object string, uploadID string, uploadedParts []minio.CompletePart) (oi minio.ObjectInfo, err error) { func (l *b2Objects) CompleteMultipartUpload(ctx context.Context, bucket string, object string, uploadID string, uploadedParts []minio.CompletePart, opts minio.ObjectOptions) (oi minio.ObjectInfo, err error) {
bkt, err := l.Bucket(ctx, bucket) bkt, err := l.Bucket(ctx, bucket)
if err != nil { if err != nil {
return oi, err return oi, err
+46 -46
View File
@@ -40,7 +40,6 @@ import (
miniogopolicy "github.com/minio/minio-go/pkg/policy" miniogopolicy "github.com/minio/minio-go/pkg/policy"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/auth"
"github.com/minio/minio/pkg/hash"
"github.com/minio/minio/pkg/policy" "github.com/minio/minio/pkg/policy"
"github.com/minio/minio/pkg/policy/condition" "github.com/minio/minio/pkg/policy/condition"
@@ -148,7 +147,7 @@ EXAMPLES:
minio.RegisterGatewayCommand(cli.Command{ minio.RegisterGatewayCommand(cli.Command{
Name: gcsBackend, Name: gcsBackend,
Usage: "Google Cloud Storage.", Usage: "Google Cloud Storage",
Action: gcsGatewayMain, Action: gcsGatewayMain,
CustomHelpTemplate: gcsGatewayTemplate, CustomHelpTemplate: gcsGatewayTemplate,
HideHelpCommand: true, HideHelpCommand: true,
@@ -207,11 +206,10 @@ func (g *GCS) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error)
gcs := &gcsGateway{ gcs := &gcsGateway{
client: client, client: client,
projectID: g.projectID, projectID: g.projectID,
ctx: ctx,
} }
// Start background process to cleanup old files in minio.sys.tmp // Start background process to cleanup old files in minio.sys.tmp
go gcs.CleanupGCSMinioSysTmp() go gcs.CleanupGCSMinioSysTmp(ctx)
return gcs, nil return gcs, nil
} }
@@ -350,7 +348,6 @@ type gcsGateway struct {
minio.GatewayUnsupported minio.GatewayUnsupported
client *storage.Client client *storage.Client
projectID string projectID string
ctx context.Context
} }
// Returns projectID from the GOOGLE_APPLICATION_CREDENTIALS file. // Returns projectID from the GOOGLE_APPLICATION_CREDENTIALS file.
@@ -367,8 +364,8 @@ func gcsParseProjectID(credsFile string) (projectID string, err error) {
} }
// Cleanup old files in minio.sys.tmp of the given bucket. // Cleanup old files in minio.sys.tmp of the given bucket.
func (l *gcsGateway) CleanupGCSMinioSysTmpBucket(bucket string) { func (l *gcsGateway) CleanupGCSMinioSysTmpBucket(ctx context.Context, bucket string) {
it := l.client.Bucket(bucket).Objects(l.ctx, &storage.Query{Prefix: minio.GatewayMinioSysTmp, Versions: false}) it := l.client.Bucket(bucket).Objects(ctx, &storage.Query{Prefix: minio.GatewayMinioSysTmp, Versions: false})
for { for {
attrs, err := it.Next() attrs, err := it.Next()
if err != nil { if err != nil {
@@ -381,7 +378,7 @@ func (l *gcsGateway) CleanupGCSMinioSysTmpBucket(bucket string) {
} }
if time.Since(attrs.Updated) > gcsMultipartExpiry { if time.Since(attrs.Updated) > gcsMultipartExpiry {
// Delete files older than 2 weeks. // Delete files older than 2 weeks.
err := l.client.Bucket(bucket).Object(attrs.Name).Delete(l.ctx) err := l.client.Bucket(bucket).Object(attrs.Name).Delete(ctx)
if err != nil { if err != nil {
reqInfo := &logger.ReqInfo{BucketName: bucket, ObjectName: attrs.Name} reqInfo := &logger.ReqInfo{BucketName: bucket, ObjectName: attrs.Name}
ctx := logger.SetReqInfo(context.Background(), reqInfo) ctx := logger.SetReqInfo(context.Background(), reqInfo)
@@ -393,9 +390,9 @@ func (l *gcsGateway) CleanupGCSMinioSysTmpBucket(bucket string) {
} }
// Cleanup old files in minio.sys.tmp of all buckets. // Cleanup old files in minio.sys.tmp of all buckets.
func (l *gcsGateway) CleanupGCSMinioSysTmp() { func (l *gcsGateway) CleanupGCSMinioSysTmp(ctx context.Context) {
for { for {
it := l.client.Buckets(l.ctx, l.projectID) it := l.client.Buckets(ctx, l.projectID)
for { for {
attrs, err := it.Next() attrs, err := it.Next()
if err != nil { if err != nil {
@@ -405,7 +402,7 @@ func (l *gcsGateway) CleanupGCSMinioSysTmp() {
} }
break break
} }
l.CleanupGCSMinioSysTmpBucket(attrs.Name) l.CleanupGCSMinioSysTmpBucket(ctx, attrs.Name)
} }
// Run the cleanup loop every 1 day. // Run the cleanup loop every 1 day.
time.Sleep(gcsCleanupInterval) time.Sleep(gcsCleanupInterval)
@@ -432,7 +429,7 @@ func (l *gcsGateway) MakeBucketWithLocation(ctx context.Context, bucket, locatio
location = "us" location = "us"
} }
err := bkt.Create(l.ctx, l.projectID, &storage.BucketAttrs{ err := bkt.Create(ctx, l.projectID, &storage.BucketAttrs{
Location: location, Location: location,
}) })
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
@@ -441,7 +438,7 @@ func (l *gcsGateway) MakeBucketWithLocation(ctx context.Context, bucket, locatio
// GetBucketInfo - Get bucket metadata.. // GetBucketInfo - Get bucket metadata..
func (l *gcsGateway) GetBucketInfo(ctx context.Context, bucket string) (minio.BucketInfo, error) { func (l *gcsGateway) GetBucketInfo(ctx context.Context, bucket string) (minio.BucketInfo, error) {
attrs, err := l.client.Bucket(bucket).Attrs(l.ctx) attrs, err := l.client.Bucket(bucket).Attrs(ctx)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return minio.BucketInfo{}, gcsToObjectError(err, bucket) return minio.BucketInfo{}, gcsToObjectError(err, bucket)
@@ -455,7 +452,7 @@ func (l *gcsGateway) GetBucketInfo(ctx context.Context, bucket string) (minio.Bu
// ListBuckets lists all buckets under your project-id on GCS. // ListBuckets lists all buckets under your project-id on GCS.
func (l *gcsGateway) ListBuckets(ctx context.Context) (buckets []minio.BucketInfo, err error) { func (l *gcsGateway) ListBuckets(ctx context.Context) (buckets []minio.BucketInfo, err error) {
it := l.client.Buckets(l.ctx, l.projectID) it := l.client.Buckets(ctx, l.projectID)
// Iterate and capture all the buckets. // Iterate and capture all the buckets.
for { for {
@@ -480,7 +477,7 @@ func (l *gcsGateway) ListBuckets(ctx context.Context) (buckets []minio.BucketInf
// DeleteBucket delete a bucket on GCS. // DeleteBucket delete a bucket on GCS.
func (l *gcsGateway) DeleteBucket(ctx context.Context, bucket string) error { func (l *gcsGateway) DeleteBucket(ctx context.Context, bucket string) error {
itObject := l.client.Bucket(bucket).Objects(l.ctx, &storage.Query{ itObject := l.client.Bucket(bucket).Objects(ctx, &storage.Query{
Delimiter: "/", Delimiter: "/",
Versions: false, Versions: false,
}) })
@@ -510,7 +507,7 @@ func (l *gcsGateway) DeleteBucket(ctx context.Context, bucket string) error {
} }
if gcsMinioPathFound { if gcsMinioPathFound {
// Remove minio.sys.tmp before deleting the bucket. // Remove minio.sys.tmp before deleting the bucket.
itObject = l.client.Bucket(bucket).Objects(l.ctx, &storage.Query{Versions: false, Prefix: minio.GatewayMinioSysTmp}) itObject = l.client.Bucket(bucket).Objects(ctx, &storage.Query{Versions: false, Prefix: minio.GatewayMinioSysTmp})
for { for {
objAttrs, err := itObject.Next() objAttrs, err := itObject.Next()
if err == iterator.Done { if err == iterator.Done {
@@ -520,14 +517,14 @@ func (l *gcsGateway) DeleteBucket(ctx context.Context, bucket string) error {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return gcsToObjectError(err) return gcsToObjectError(err)
} }
err = l.client.Bucket(bucket).Object(objAttrs.Name).Delete(l.ctx) err = l.client.Bucket(bucket).Object(objAttrs.Name).Delete(ctx)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return gcsToObjectError(err) return gcsToObjectError(err)
} }
} }
} }
err := l.client.Bucket(bucket).Delete(l.ctx) err := l.client.Bucket(bucket).Delete(ctx)
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return gcsToObjectError(err, bucket) return gcsToObjectError(err, bucket)
} }
@@ -562,7 +559,7 @@ func (l *gcsGateway) ListObjects(ctx context.Context, bucket string, prefix stri
return minio.ListObjectsInfo{}, nil return minio.ListObjectsInfo{}, nil
} }
it := l.client.Bucket(bucket).Objects(l.ctx, &storage.Query{ it := l.client.Bucket(bucket).Objects(ctx, &storage.Query{
Delimiter: delimiter, Delimiter: delimiter,
Prefix: prefix, Prefix: prefix,
Versions: false, Versions: false,
@@ -669,7 +666,7 @@ func (l *gcsGateway) ListObjectsV2(ctx context.Context, bucket, prefix, continua
return minio.ListObjectsV2Info{ContinuationToken: continuationToken}, nil return minio.ListObjectsV2Info{ContinuationToken: continuationToken}, nil
} }
it := l.client.Bucket(bucket).Objects(l.ctx, &storage.Query{ it := l.client.Bucket(bucket).Objects(ctx, &storage.Query{
Delimiter: delimiter, Delimiter: delimiter,
Prefix: prefix, Prefix: prefix,
Versions: false, Versions: false,
@@ -770,7 +767,7 @@ func (l *gcsGateway) GetObjectNInfo(ctx context.Context, bucket, object string,
func (l *gcsGateway) GetObject(ctx context.Context, bucket string, key string, startOffset int64, length int64, writer io.Writer, etag string, opts minio.ObjectOptions) error { func (l *gcsGateway) GetObject(ctx context.Context, bucket string, key string, startOffset int64, length int64, writer io.Writer, etag string, opts minio.ObjectOptions) error {
// if we want to mimic S3 behavior exactly, we need to verify if bucket exists first, // if we want to mimic S3 behavior exactly, we need to verify if bucket exists first,
// otherwise gcs will just return object not exist in case of non-existing bucket // otherwise gcs will just return object not exist in case of non-existing bucket
if _, err := l.client.Bucket(bucket).Attrs(l.ctx); err != nil { if _, err := l.client.Bucket(bucket).Attrs(ctx); err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return gcsToObjectError(err, bucket) return gcsToObjectError(err, bucket)
} }
@@ -782,7 +779,7 @@ func (l *gcsGateway) GetObject(ctx context.Context, bucket string, key string, s
// Calling ReadCompressed with true accomplishes that. // Calling ReadCompressed with true accomplishes that.
object := l.client.Bucket(bucket).Object(key).ReadCompressed(true) object := l.client.Bucket(bucket).Object(key).ReadCompressed(true)
r, err := object.NewRangeReader(l.ctx, startOffset, length) r, err := object.NewRangeReader(ctx, startOffset, length)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return gcsToObjectError(err, bucket, key) return gcsToObjectError(err, bucket, key)
@@ -865,12 +862,12 @@ func applyMetadataToGCSAttrs(metadata map[string]string, attrs *storage.ObjectAt
func (l *gcsGateway) GetObjectInfo(ctx context.Context, bucket string, object string, opts minio.ObjectOptions) (minio.ObjectInfo, error) { func (l *gcsGateway) GetObjectInfo(ctx context.Context, bucket string, object string, opts minio.ObjectOptions) (minio.ObjectInfo, error) {
// if we want to mimic S3 behavior exactly, we need to verify if bucket exists first, // if we want to mimic S3 behavior exactly, we need to verify if bucket exists first,
// otherwise gcs will just return object not exist in case of non-existing bucket // otherwise gcs will just return object not exist in case of non-existing bucket
if _, err := l.client.Bucket(bucket).Attrs(l.ctx); err != nil { if _, err := l.client.Bucket(bucket).Attrs(ctx); err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return minio.ObjectInfo{}, gcsToObjectError(err, bucket) return minio.ObjectInfo{}, gcsToObjectError(err, bucket)
} }
attrs, err := l.client.Bucket(bucket).Object(object).Attrs(l.ctx) attrs, err := l.client.Bucket(bucket).Object(object).Attrs(ctx)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return minio.ObjectInfo{}, gcsToObjectError(err, bucket, object) return minio.ObjectInfo{}, gcsToObjectError(err, bucket, object)
@@ -880,17 +877,19 @@ func (l *gcsGateway) GetObjectInfo(ctx context.Context, bucket string, object st
} }
// PutObject - Create a new object with the incoming data, // PutObject - Create a new object with the incoming data,
func (l *gcsGateway) PutObject(ctx context.Context, bucket string, key string, data *hash.Reader, metadata map[string]string, opts minio.ObjectOptions) (minio.ObjectInfo, error) { func (l *gcsGateway) PutObject(ctx context.Context, bucket string, key string, r *minio.PutObjReader, metadata map[string]string, opts minio.ObjectOptions) (minio.ObjectInfo, error) {
data := r.Reader
// if we want to mimic S3 behavior exactly, we need to verify if bucket exists first, // if we want to mimic S3 behavior exactly, we need to verify if bucket exists first,
// otherwise gcs will just return object not exist in case of non-existing bucket // otherwise gcs will just return object not exist in case of non-existing bucket
if _, err := l.client.Bucket(bucket).Attrs(l.ctx); err != nil { if _, err := l.client.Bucket(bucket).Attrs(ctx); err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return minio.ObjectInfo{}, gcsToObjectError(err, bucket) return minio.ObjectInfo{}, gcsToObjectError(err, bucket)
} }
object := l.client.Bucket(bucket).Object(key) object := l.client.Bucket(bucket).Object(key)
w := object.NewWriter(l.ctx) w := object.NewWriter(ctx)
// Disable "chunked" uploading in GCS client if the size of the data to be uploaded is below // Disable "chunked" uploading in GCS client if the size of the data to be uploaded is below
// the current chunk-size of the writer. This avoids an unnecessary memory allocation. // the current chunk-size of the writer. This avoids an unnecessary memory allocation.
if data.Size() < int64(w.ChunkSize) { if data.Size() < int64(w.ChunkSize) {
@@ -908,7 +907,7 @@ func (l *gcsGateway) PutObject(ctx context.Context, bucket string, key string, d
// Close the object writer upon success. // Close the object writer upon success.
w.Close() w.Close()
attrs, err := object.Attrs(l.ctx) attrs, err := object.Attrs(ctx)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return minio.ObjectInfo{}, gcsToObjectError(err, bucket, key) return minio.ObjectInfo{}, gcsToObjectError(err, bucket, key)
@@ -927,7 +926,7 @@ func (l *gcsGateway) CopyObject(ctx context.Context, srcBucket string, srcObject
copier := dst.CopierFrom(src) copier := dst.CopierFrom(src)
applyMetadataToGCSAttrs(srcInfo.UserDefined, &copier.ObjectAttrs) applyMetadataToGCSAttrs(srcInfo.UserDefined, &copier.ObjectAttrs)
attrs, err := copier.Run(l.ctx) attrs, err := copier.Run(ctx)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return minio.ObjectInfo{}, gcsToObjectError(err, destBucket, destObject) return minio.ObjectInfo{}, gcsToObjectError(err, destBucket, destObject)
@@ -938,7 +937,7 @@ func (l *gcsGateway) CopyObject(ctx context.Context, srcBucket string, srcObject
// DeleteObject - Deletes a blob in bucket // DeleteObject - Deletes a blob in bucket
func (l *gcsGateway) DeleteObject(ctx context.Context, bucket string, object string) error { func (l *gcsGateway) DeleteObject(ctx context.Context, bucket string, object string) error {
err := l.client.Bucket(bucket).Object(object).Delete(l.ctx) err := l.client.Bucket(bucket).Object(object).Delete(ctx)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return gcsToObjectError(err, bucket, object) return gcsToObjectError(err, bucket, object)
@@ -955,7 +954,7 @@ func (l *gcsGateway) NewMultipartUpload(ctx context.Context, bucket string, key
// generate name for part zero // generate name for part zero
meta := gcsMultipartMetaName(uploadID) meta := gcsMultipartMetaName(uploadID)
w := l.client.Bucket(bucket).Object(meta).NewWriter(l.ctx) w := l.client.Bucket(bucket).Object(meta).NewWriter(ctx)
defer w.Close() defer w.Close()
applyMetadataToGCSAttrs(metadata, &w.ObjectAttrs) applyMetadataToGCSAttrs(metadata, &w.ObjectAttrs)
@@ -1055,13 +1054,14 @@ func (l *gcsGateway) ListMultipartUploads(ctx context.Context, bucket string, pr
// Checks if minio.sys.tmp/multipart/v1/<upload-id>/gcs.json exists, returns // Checks if minio.sys.tmp/multipart/v1/<upload-id>/gcs.json exists, returns
// an object layer compatible error upon any error. // an object layer compatible error upon any error.
func (l *gcsGateway) checkUploadIDExists(ctx context.Context, bucket string, key string, uploadID string) error { func (l *gcsGateway) checkUploadIDExists(ctx context.Context, bucket string, key string, uploadID string) error {
_, err := l.client.Bucket(bucket).Object(gcsMultipartMetaName(uploadID)).Attrs(l.ctx) _, err := l.client.Bucket(bucket).Object(gcsMultipartMetaName(uploadID)).Attrs(ctx)
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return gcsToObjectError(err, bucket, key, uploadID) return gcsToObjectError(err, bucket, key, uploadID)
} }
// PutObjectPart puts a part of object in bucket // PutObjectPart puts a part of object in bucket
func (l *gcsGateway) PutObjectPart(ctx context.Context, bucket string, key string, uploadID string, partNumber int, data *hash.Reader, opts minio.ObjectOptions) (minio.PartInfo, error) { func (l *gcsGateway) PutObjectPart(ctx context.Context, bucket string, key string, uploadID string, partNumber int, r *minio.PutObjReader, opts minio.ObjectOptions) (minio.PartInfo, error) {
data := r.Reader
if err := l.checkUploadIDExists(ctx, bucket, key, uploadID); err != nil { if err := l.checkUploadIDExists(ctx, bucket, key, uploadID); err != nil {
return minio.PartInfo{}, err return minio.PartInfo{}, err
} }
@@ -1071,7 +1071,7 @@ func (l *gcsGateway) PutObjectPart(ctx context.Context, bucket string, key strin
etag = minio.GenETag() etag = minio.GenETag()
} }
object := l.client.Bucket(bucket).Object(gcsMultipartDataName(uploadID, partNumber, etag)) object := l.client.Bucket(bucket).Object(gcsMultipartDataName(uploadID, partNumber, etag))
w := object.NewWriter(l.ctx) w := object.NewWriter(ctx)
// Disable "chunked" uploading in GCS client. If enabled, it can cause a corner case // Disable "chunked" uploading in GCS client. If enabled, it can cause a corner case
// where it tries to upload 0 bytes in the last chunk and get error from server. // where it tries to upload 0 bytes in the last chunk and get error from server.
w.ChunkSize = 0 w.ChunkSize = 0
@@ -1184,7 +1184,7 @@ func (l *gcsGateway) cleanupMultipartUpload(ctx context.Context, bucket, key, up
prefix := fmt.Sprintf("%s/%s/", gcsMinioMultipartPathV1, uploadID) prefix := fmt.Sprintf("%s/%s/", gcsMinioMultipartPathV1, uploadID)
// iterate through all parts and delete them // iterate through all parts and delete them
it := l.client.Bucket(bucket).Objects(l.ctx, &storage.Query{Prefix: prefix, Versions: false}) it := l.client.Bucket(bucket).Objects(ctx, &storage.Query{Prefix: prefix, Versions: false})
for { for {
attrs, err := it.Next() attrs, err := it.Next()
@@ -1198,7 +1198,7 @@ func (l *gcsGateway) cleanupMultipartUpload(ctx context.Context, bucket, key, up
object := l.client.Bucket(bucket).Object(attrs.Name) object := l.client.Bucket(bucket).Object(attrs.Name)
// Ignore the error as parallel AbortMultipartUpload might have deleted it. // Ignore the error as parallel AbortMultipartUpload might have deleted it.
object.Delete(l.ctx) object.Delete(ctx)
} }
return nil return nil
@@ -1216,17 +1216,17 @@ func (l *gcsGateway) AbortMultipartUpload(ctx context.Context, bucket string, ke
// Note that there is a limit (currently 32) to the number of components that can // Note that there is a limit (currently 32) to the number of components that can
// be composed in a single operation. There is a per-project rate limit (currently 200) // be composed in a single operation. There is a per-project rate limit (currently 200)
// to the number of source objects you can compose per second. // to the number of source objects you can compose per second.
func (l *gcsGateway) CompleteMultipartUpload(ctx context.Context, bucket string, key string, uploadID string, uploadedParts []minio.CompletePart) (minio.ObjectInfo, error) { func (l *gcsGateway) CompleteMultipartUpload(ctx context.Context, bucket string, key string, uploadID string, uploadedParts []minio.CompletePart, opts minio.ObjectOptions) (minio.ObjectInfo, error) {
meta := gcsMultipartMetaName(uploadID) meta := gcsMultipartMetaName(uploadID)
object := l.client.Bucket(bucket).Object(meta) object := l.client.Bucket(bucket).Object(meta)
partZeroAttrs, err := object.Attrs(l.ctx) partZeroAttrs, err := object.Attrs(ctx)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return minio.ObjectInfo{}, gcsToObjectError(err, bucket, key, uploadID) return minio.ObjectInfo{}, gcsToObjectError(err, bucket, key, uploadID)
} }
r, err := object.NewReader(l.ctx) r, err := object.NewReader(ctx)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return minio.ObjectInfo{}, gcsToObjectError(err, bucket, key) return minio.ObjectInfo{}, gcsToObjectError(err, bucket, key)
@@ -1257,7 +1257,7 @@ func (l *gcsGateway) CompleteMultipartUpload(ctx context.Context, bucket string,
for i, uploadedPart := range uploadedParts { for i, uploadedPart := range uploadedParts {
parts = append(parts, l.client.Bucket(bucket).Object(gcsMultipartDataName(uploadID, parts = append(parts, l.client.Bucket(bucket).Object(gcsMultipartDataName(uploadID,
uploadedPart.PartNumber, uploadedPart.ETag))) uploadedPart.PartNumber, uploadedPart.ETag)))
partAttr, pErr := l.client.Bucket(bucket).Object(gcsMultipartDataName(uploadID, uploadedPart.PartNumber, uploadedPart.ETag)).Attrs(l.ctx) partAttr, pErr := l.client.Bucket(bucket).Object(gcsMultipartDataName(uploadID, uploadedPart.PartNumber, uploadedPart.ETag)).Attrs(ctx)
if pErr != nil { if pErr != nil {
logger.LogIf(ctx, pErr) logger.LogIf(ctx, pErr)
return minio.ObjectInfo{}, gcsToObjectError(pErr, bucket, key, uploadID) return minio.ObjectInfo{}, gcsToObjectError(pErr, bucket, key, uploadID)
@@ -1303,7 +1303,7 @@ func (l *gcsGateway) CompleteMultipartUpload(ctx context.Context, bucket string,
composer.ContentType = partZeroAttrs.ContentType composer.ContentType = partZeroAttrs.ContentType
composer.Metadata = partZeroAttrs.Metadata composer.Metadata = partZeroAttrs.Metadata
if _, err = composer.Run(l.ctx); err != nil { if _, err = composer.Run(ctx); err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return minio.ObjectInfo{}, gcsToObjectError(err, bucket, key) return minio.ObjectInfo{}, gcsToObjectError(err, bucket, key)
} }
@@ -1320,7 +1320,7 @@ func (l *gcsGateway) CompleteMultipartUpload(ctx context.Context, bucket string,
composer.ContentDisposition = partZeroAttrs.ContentDisposition composer.ContentDisposition = partZeroAttrs.ContentDisposition
composer.ContentLanguage = partZeroAttrs.ContentLanguage composer.ContentLanguage = partZeroAttrs.ContentLanguage
composer.Metadata = partZeroAttrs.Metadata composer.Metadata = partZeroAttrs.Metadata
attrs, err := composer.Run(l.ctx) attrs, err := composer.Run(ctx)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return minio.ObjectInfo{}, gcsToObjectError(err, bucket, key) return minio.ObjectInfo{}, gcsToObjectError(err, bucket, key)
@@ -1360,7 +1360,7 @@ func (l *gcsGateway) SetBucketPolicy(ctx context.Context, bucket string, bucketP
acl := l.client.Bucket(bucket).ACL() acl := l.client.Bucket(bucket).ACL()
if policies[0].Policy == miniogopolicy.BucketPolicyNone { if policies[0].Policy == miniogopolicy.BucketPolicyNone {
if err := acl.Delete(l.ctx, storage.AllUsers); err != nil { if err := acl.Delete(ctx, storage.AllUsers); err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return gcsToObjectError(err, bucket) return gcsToObjectError(err, bucket)
} }
@@ -1378,7 +1378,7 @@ func (l *gcsGateway) SetBucketPolicy(ctx context.Context, bucket string, bucketP
return minio.NotImplemented{} return minio.NotImplemented{}
} }
if err := acl.Set(l.ctx, storage.AllUsers, role); err != nil { if err := acl.Set(ctx, storage.AllUsers, role); err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
return gcsToObjectError(err, bucket) return gcsToObjectError(err, bucket)
} }
@@ -1388,7 +1388,7 @@ func (l *gcsGateway) SetBucketPolicy(ctx context.Context, bucket string, bucketP
// GetBucketPolicy - Get policy on bucket // GetBucketPolicy - Get policy on bucket
func (l *gcsGateway) GetBucketPolicy(ctx context.Context, bucket string) (*policy.Policy, error) { func (l *gcsGateway) GetBucketPolicy(ctx context.Context, bucket string) (*policy.Policy, error) {
rules, err := l.client.Bucket(bucket).ACL().List(l.ctx) rules, err := l.client.Bucket(bucket).ACL().List(ctx)
if err != nil { if err != nil {
return nil, gcsToObjectError(err, bucket) return nil, gcsToObjectError(err, bucket)
} }
@@ -1447,7 +1447,7 @@ func (l *gcsGateway) GetBucketPolicy(ctx context.Context, bucket string) (*polic
// DeleteBucketPolicy - Delete all policies on bucket // DeleteBucketPolicy - Delete all policies on bucket
func (l *gcsGateway) DeleteBucketPolicy(ctx context.Context, bucket string) error { func (l *gcsGateway) DeleteBucketPolicy(ctx context.Context, bucket string) error {
// This only removes the storage.AllUsers policies // This only removes the storage.AllUsers policies
if err := l.client.Bucket(bucket).ACL().Delete(l.ctx, storage.AllUsers); err != nil { if err := l.client.Bucket(bucket).ACL().Delete(ctx, storage.AllUsers); err != nil {
return gcsToObjectError(err, bucket) return gcsToObjectError(err, bucket)
} }
+3 -3
View File
@@ -35,7 +35,6 @@ import (
minio "github.com/minio/minio/cmd" minio "github.com/minio/minio/cmd"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/auth"
"github.com/minio/minio/pkg/hash"
) )
// stor is a namespace within manta where you store any documents that are deemed as private // stor is a namespace within manta where you store any documents that are deemed as private
@@ -109,7 +108,7 @@ EXAMPLES:
minio.RegisterGatewayCommand(cli.Command{ minio.RegisterGatewayCommand(cli.Command{
Name: mantaBackend, Name: mantaBackend,
Usage: "Manta Object Storage.", Usage: "Manta Object Storage",
Action: mantaGatewayMain, Action: mantaGatewayMain,
CustomHelpTemplate: mantaGatewayTemplate, CustomHelpTemplate: mantaGatewayTemplate,
HideHelpCommand: true, HideHelpCommand: true,
@@ -608,7 +607,8 @@ func (d dummySeeker) Seek(offset int64, whence int) (int64, error) {
// CreateBlockBlobFromReader. // CreateBlockBlobFromReader.
// //
// https://apidocs.joyent.com/manta/api.html#PutObject // https://apidocs.joyent.com/manta/api.html#PutObject
func (t *tritonObjects) PutObject(ctx context.Context, bucket, object string, data *hash.Reader, metadata map[string]string, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) { func (t *tritonObjects) PutObject(ctx context.Context, bucket, object string, r *minio.PutObjReader, metadata map[string]string, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) {
data := r.Reader
if err = t.client.Objects().Put(ctx, &storage.PutObjectInput{ if err = t.client.Objects().Put(ctx, &storage.PutObjectInput{
ContentLength: uint64(data.Size()), ContentLength: uint64(data.Size()),
ObjectPath: path.Join(mantaRoot, bucket, object), ObjectPath: path.Join(mantaRoot, bucket, object),
+8 -13
View File
@@ -74,7 +74,7 @@ EXAMPLES:
minio.RegisterGatewayCommand(cli.Command{ minio.RegisterGatewayCommand(cli.Command{
Name: nasBackend, Name: nasBackend,
Usage: "Network-attached storage (NAS).", Usage: "Network-attached storage (NAS)",
Action: nasGatewayMain, Action: nasGatewayMain,
CustomHelpTemplate: nasGatewayTemplate, CustomHelpTemplate: nasGatewayTemplate,
HideHelpCommand: true, HideHelpCommand: true,
@@ -108,7 +108,7 @@ func (g *NAS) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &nasObjects{newObject.(*minio.FSObjects)}, nil return &nasObjects{newObject}, nil
} }
// Production - nas gateway is production ready. // Production - nas gateway is production ready.
@@ -116,17 +116,12 @@ func (g *NAS) Production() bool {
return true return true
} }
// IsListenBucketSupported returns whether listen bucket notification is applicable for this gateway.
func (g *NAS) IsListenBucketSupported() bool {
return false
}
// nasObjects implements gateway for Minio and S3 compatible object storage servers. // nasObjects implements gateway for Minio and S3 compatible object storage servers.
type nasObjects struct { type nasObjects struct {
*minio.FSObjects minio.ObjectLayer
}
// IsNotificationSupported returns whether notifications are applicable for this layer.
func (l *nasObjects) IsNotificationSupported() bool {
return false
}
// IsCompressionSupported returns whether compression is applicable for this layer.
func (l *nasObjects) IsCompressionSupported() bool {
return false
} }
+7 -4
View File
@@ -98,7 +98,7 @@ EXAMPLES:
minio.RegisterGatewayCommand(cli.Command{ minio.RegisterGatewayCommand(cli.Command{
Name: "oss", Name: "oss",
Usage: "Alibaba Cloud (Aliyun) Object Storage Service (OSS).", Usage: "Alibaba Cloud (Aliyun) Object Storage Service (OSS)",
Action: ossGatewayMain, Action: ossGatewayMain,
CustomHelpTemplate: ossGatewayTemplate, CustomHelpTemplate: ossGatewayTemplate,
HideHelpCommand: true, HideHelpCommand: true,
@@ -655,7 +655,9 @@ func ossPutObject(ctx context.Context, client *oss.Client, bucket, object string
} }
// PutObject creates a new object with the incoming data. // PutObject creates a new object with the incoming data.
func (l *ossObjects) PutObject(ctx context.Context, bucket, object string, data *hash.Reader, metadata map[string]string, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) { func (l *ossObjects) PutObject(ctx context.Context, bucket, object string, r *minio.PutObjReader, metadata map[string]string, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) {
data := r.Reader
return ossPutObject(ctx, l.Client, bucket, object, data, metadata) return ossPutObject(ctx, l.Client, bucket, object, data, metadata)
} }
@@ -773,7 +775,8 @@ func (l *ossObjects) NewMultipartUpload(ctx context.Context, bucket, object stri
} }
// PutObjectPart puts a part of object in bucket. // PutObjectPart puts a part of object in bucket.
func (l *ossObjects) PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, data *hash.Reader, opts minio.ObjectOptions) (pi minio.PartInfo, err error) { func (l *ossObjects) PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, r *minio.PutObjReader, opts minio.ObjectOptions) (pi minio.PartInfo, err error) {
data := r.Reader
bkt, err := l.Client.Bucket(bucket) bkt, err := l.Client.Bucket(bucket)
if err != nil { if err != nil {
logger.LogIf(ctx, err) logger.LogIf(ctx, err)
@@ -914,7 +917,7 @@ func (l *ossObjects) AbortMultipartUpload(ctx context.Context, bucket, object, u
} }
// CompleteMultipartUpload completes ongoing multipart upload and finalizes object. // CompleteMultipartUpload completes ongoing multipart upload and finalizes object.
func (l *ossObjects) CompleteMultipartUpload(ctx context.Context, bucket, object, uploadID string, uploadedParts []minio.CompletePart) (oi minio.ObjectInfo, err error) { func (l *ossObjects) CompleteMultipartUpload(ctx context.Context, bucket, object, uploadID string, uploadedParts []minio.CompletePart, opts minio.ObjectOptions) (oi minio.ObjectInfo, err error) {
client := l.Client client := l.Client
bkt, err := client.Bucket(bucket) bkt, err := client.Bucket(bucket)
if err != nil { if err != nil {
+8 -7
View File
@@ -31,7 +31,6 @@ import (
"github.com/minio/minio-go/pkg/s3utils" "github.com/minio/minio-go/pkg/s3utils"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/auth"
"github.com/minio/minio/pkg/hash"
"github.com/minio/minio/pkg/policy" "github.com/minio/minio/pkg/policy"
minio "github.com/minio/minio/cmd" minio "github.com/minio/minio/cmd"
@@ -112,7 +111,7 @@ EXAMPLES:
minio.RegisterGatewayCommand(cli.Command{ minio.RegisterGatewayCommand(cli.Command{
Name: s3Backend, Name: s3Backend,
Usage: "Amazon Simple Storage Service (S3).", Usage: "Amazon Simple Storage Service (S3)",
Action: s3GatewayMain, Action: s3GatewayMain,
CustomHelpTemplate: s3GatewayTemplate, CustomHelpTemplate: s3GatewayTemplate,
HideHelpCommand: true, HideHelpCommand: true,
@@ -406,7 +405,8 @@ func (l *s3Objects) GetObjectInfo(ctx context.Context, bucket string, object str
} }
// PutObject creates a new object with the incoming data, // PutObject creates a new object with the incoming data,
func (l *s3Objects) PutObject(ctx context.Context, bucket string, object string, data *hash.Reader, metadata map[string]string, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) { func (l *s3Objects) PutObject(ctx context.Context, bucket string, object string, r *minio.PutObjReader, metadata map[string]string, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) {
data := r.Reader
oi, err := l.Client.PutObject(bucket, object, data, data.Size(), data.MD5Base64String(), data.SHA256HexString(), minio.ToMinioClientMetadata(metadata), opts.ServerSideEncryption) oi, err := l.Client.PutObject(bucket, object, data, data.Size(), data.MD5Base64String(), data.SHA256HexString(), minio.ToMinioClientMetadata(metadata), opts.ServerSideEncryption)
if err != nil { if err != nil {
return objInfo, minio.ErrorRespToObjectError(err, bucket, object) return objInfo, minio.ErrorRespToObjectError(err, bucket, object)
@@ -464,7 +464,8 @@ func (l *s3Objects) NewMultipartUpload(ctx context.Context, bucket string, objec
} }
// PutObjectPart puts a part of object in bucket // PutObjectPart puts a part of object in bucket
func (l *s3Objects) PutObjectPart(ctx context.Context, bucket string, object string, uploadID string, partID int, data *hash.Reader, opts minio.ObjectOptions) (pi minio.PartInfo, e error) { func (l *s3Objects) PutObjectPart(ctx context.Context, bucket string, object string, uploadID string, partID int, r *minio.PutObjReader, opts minio.ObjectOptions) (pi minio.PartInfo, e error) {
data := r.Reader
info, err := l.Client.PutObjectPart(bucket, object, uploadID, partID, data, data.Size(), data.MD5Base64String(), data.SHA256HexString(), opts.ServerSideEncryption) info, err := l.Client.PutObjectPart(bucket, object, uploadID, partID, data, data.Size(), data.MD5Base64String(), data.SHA256HexString(), opts.ServerSideEncryption)
if err != nil { if err != nil {
return pi, minio.ErrorRespToObjectError(err, bucket, object) return pi, minio.ErrorRespToObjectError(err, bucket, object)
@@ -508,13 +509,13 @@ func (l *s3Objects) AbortMultipartUpload(ctx context.Context, bucket string, obj
} }
// CompleteMultipartUpload completes ongoing multipart upload and finalizes object // CompleteMultipartUpload completes ongoing multipart upload and finalizes object
func (l *s3Objects) CompleteMultipartUpload(ctx context.Context, bucket string, object string, uploadID string, uploadedParts []minio.CompletePart) (oi minio.ObjectInfo, e error) { func (l *s3Objects) CompleteMultipartUpload(ctx context.Context, bucket string, object string, uploadID string, uploadedParts []minio.CompletePart, opts minio.ObjectOptions) (oi minio.ObjectInfo, e error) {
err := l.Client.CompleteMultipartUpload(bucket, object, uploadID, minio.ToMinioClientCompleteParts(uploadedParts)) etag, err := l.Client.CompleteMultipartUpload(bucket, object, uploadID, minio.ToMinioClientCompleteParts(uploadedParts))
if err != nil { if err != nil {
return oi, minio.ErrorRespToObjectError(err, bucket, object) return oi, minio.ErrorRespToObjectError(err, bucket, object)
} }
return l.GetObjectInfo(ctx, bucket, object, minio.ObjectOptions{}) return minio.ObjectInfo{Bucket: bucket, Name: object, ETag: etag}, nil
} }
// SetBucketPolicy sets policy on bucket // SetBucketPolicy sets policy on bucket
+3 -3
View File
@@ -38,7 +38,6 @@ import (
minio "github.com/minio/minio/cmd" minio "github.com/minio/minio/cmd"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/auth"
"github.com/minio/minio/pkg/hash"
"github.com/minio/sha256-simd" "github.com/minio/sha256-simd"
) )
@@ -98,7 +97,7 @@ EXAMPLES:
minio.RegisterGatewayCommand(cli.Command{ minio.RegisterGatewayCommand(cli.Command{
Name: siaBackend, Name: siaBackend,
Usage: "Sia Decentralized Cloud.", Usage: "Sia Decentralized Cloud",
Action: siaGatewayMain, Action: siaGatewayMain,
CustomHelpTemplate: siaGatewayTemplate, CustomHelpTemplate: siaGatewayTemplate,
HideHelpCommand: true, HideHelpCommand: true,
@@ -554,7 +553,8 @@ func (s *siaObjects) GetObjectInfo(ctx context.Context, bucket string, object st
} }
// PutObject creates a new object with the incoming data, // PutObject creates a new object with the incoming data,
func (s *siaObjects) PutObject(ctx context.Context, bucket string, object string, data *hash.Reader, metadata map[string]string, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) { func (s *siaObjects) PutObject(ctx context.Context, bucket string, object string, r *minio.PutObjReader, metadata map[string]string, opts minio.ObjectOptions) (objInfo minio.ObjectInfo, err error) {
data := r.Reader
srcFile := path.Join(s.TempDir, minio.MustGetUUID()) srcFile := path.Join(s.TempDir, minio.MustGetUUID())
writer, err := os.Create(srcFile) writer, err := os.Create(srcFile)
if err != nil { if err != nil {
+33 -29
View File
@@ -91,7 +91,7 @@ func setRequestHeaderSizeLimitHandler(h http.Handler) http.Handler {
// of the user-defined metadata to 2 KB. // of the user-defined metadata to 2 KB.
func (h requestHeaderSizeLimitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h requestHeaderSizeLimitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if isHTTPHeaderSizeTooLarge(r.Header) { if isHTTPHeaderSizeTooLarge(r.Header) {
writeErrorResponse(w, ErrMetadataTooLarge, r.URL) writeErrorResponse(w, ErrMetadataTooLarge, r.URL, guessIsBrowserReq(r))
return return
} }
h.Handler.ServeHTTP(w, r) h.Handler.ServeHTTP(w, r)
@@ -134,7 +134,7 @@ func filterReservedMetadata(h http.Handler) http.Handler {
// would be treated as metadata. // would be treated as metadata.
func (h reservedMetadataHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h reservedMetadataHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if containsReservedMetadata(r.Header) { if containsReservedMetadata(r.Header) {
writeErrorResponse(w, ErrUnsupportedMetadata, r.URL) writeErrorResponse(w, ErrUnsupportedMetadata, r.URL, guessIsBrowserReq(r))
return return
} }
h.Handler.ServeHTTP(w, r) h.Handler.ServeHTTP(w, r)
@@ -228,7 +228,8 @@ func guessIsRPCReq(req *http.Request) bool {
if req == nil { if req == nil {
return false return false
} }
return req.Method == http.MethodPost return req.Method == http.MethodPost &&
strings.HasPrefix(req.URL.Path, minioReservedBucketPath+"/")
} }
func (h redirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h redirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -300,7 +301,7 @@ func (h minioReservedBucketHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
// buckets // buckets
bucketName, _ := urlPath2BucketObjectName(r.URL.Path) bucketName, _ := urlPath2BucketObjectName(r.URL.Path)
if isMinioReservedBucket(bucketName) || isMinioMetaBucket(bucketName) { if isMinioReservedBucket(bucketName) || isMinioMetaBucket(bucketName) {
writeErrorResponse(w, ErrAllAccessDisabled, r.URL) writeErrorResponse(w, ErrAllAccessDisabled, r.URL, guessIsBrowserReq(r))
return return
} }
} }
@@ -363,14 +364,14 @@ func (h timeValidityHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// All our internal APIs are sensitive towards Date // All our internal APIs are sensitive towards Date
// header, for all requests where Date header is not // header, for all requests where Date header is not
// present we will reject such clients. // present we will reject such clients.
writeErrorResponse(w, apiErr, r.URL) writeErrorResponse(w, apiErr, r.URL, guessIsBrowserReq(r))
return return
} }
// Verify if the request date header is shifted by less than globalMaxSkewTime parameter in the past // Verify if the request date header is shifted by less than globalMaxSkewTime parameter in the past
// or in the future, reject request otherwise. // or in the future, reject request otherwise.
curTime := UTCNow() curTime := UTCNow()
if curTime.Sub(amzDate) > globalMaxSkewTime || amzDate.Sub(curTime) > globalMaxSkewTime { if curTime.Sub(amzDate) > globalMaxSkewTime || amzDate.Sub(curTime) > globalMaxSkewTime {
writeErrorResponse(w, ErrRequestTimeTooSkewed, r.URL) writeErrorResponse(w, ErrRequestTimeTooSkewed, r.URL, guessIsBrowserReq(r))
return return
} }
} }
@@ -485,20 +486,20 @@ func (h resourceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// If bucketName is present and not objectName check for bucket level resource queries. // If bucketName is present and not objectName check for bucket level resource queries.
if bucketName != "" && objectName == "" { if bucketName != "" && objectName == "" {
if ignoreNotImplementedBucketResources(r) { if ignoreNotImplementedBucketResources(r) {
writeErrorResponse(w, ErrNotImplemented, r.URL) writeErrorResponse(w, ErrNotImplemented, r.URL, guessIsBrowserReq(r))
return return
} }
} }
// If bucketName and objectName are present check for its resource queries. // If bucketName and objectName are present check for its resource queries.
if bucketName != "" && objectName != "" { if bucketName != "" && objectName != "" {
if ignoreNotImplementedObjectResources(r) { if ignoreNotImplementedObjectResources(r) {
writeErrorResponse(w, ErrNotImplemented, r.URL) writeErrorResponse(w, ErrNotImplemented, r.URL, guessIsBrowserReq(r))
return return
} }
} }
// A put method on path "/" doesn't make sense, ignore it. // A put method on path "/" doesn't make sense, ignore it.
if r.Method == http.MethodPut && r.URL.Path == "/" { if r.Method == http.MethodPut && r.URL.Path == "/" {
writeErrorResponse(w, ErrNotImplemented, r.URL) writeErrorResponse(w, ErrNotImplemented, r.URL, guessIsBrowserReq(r))
return return
} }
@@ -601,14 +602,14 @@ func hasBadPathComponent(path string) bool {
func (h pathValidityHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h pathValidityHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Check for bad components in URL path. // Check for bad components in URL path.
if hasBadPathComponent(r.URL.Path) { if hasBadPathComponent(r.URL.Path) {
writeErrorResponse(w, ErrInvalidResourceName, r.URL) writeErrorResponse(w, ErrInvalidResourceName, r.URL, guessIsBrowserReq(r))
return return
} }
// Check for bad components in URL query values. // Check for bad components in URL query values.
for _, vv := range r.URL.Query() { for _, vv := range r.URL.Query() {
for _, v := range vv { for _, v := range vv {
if hasBadPathComponent(v) { if hasBadPathComponent(v) {
writeErrorResponse(w, ErrInvalidResourceName, r.URL) writeErrorResponse(w, ErrInvalidResourceName, r.URL, guessIsBrowserReq(r))
return return
} }
} }
@@ -625,7 +626,9 @@ type bucketForwardingHandler struct {
} }
func (f bucketForwardingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (f bucketForwardingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if globalDNSConfig == nil || globalDomainName == "" || guessIsBrowserReq(r) || guessIsHealthCheckReq(r) || guessIsMetricsReq(r) || guessIsRPCReq(r) { if globalDNSConfig == nil || globalDomainName == "" ||
guessIsBrowserReq(r) || guessIsHealthCheckReq(r) ||
guessIsMetricsReq(r) || guessIsRPCReq(r) || isAdminReq(r) {
f.handler.ServeHTTP(w, r) f.handler.ServeHTTP(w, r)
return return
} }
@@ -653,9 +656,9 @@ func (f bucketForwardingHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
sr, err := globalDNSConfig.Get(bucket) sr, err := globalDNSConfig.Get(bucket)
if err != nil { if err != nil {
if err == dns.ErrNoEntriesFound { if err == dns.ErrNoEntriesFound {
writeErrorResponse(w, ErrNoSuchBucket, r.URL) writeErrorResponse(w, ErrNoSuchBucket, r.URL, guessIsBrowserReq(r))
} else { } else {
writeErrorResponse(w, toAPIErrorCode(context.Background(), err), r.URL) writeErrorResponse(w, toAPIErrorCode(context.Background(), err), r.URL, guessIsBrowserReq(r))
} }
return return
} }
@@ -716,31 +719,32 @@ func (l rateLimit) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// potential pileup on the server. // potential pileup on the server.
if err := l.Wait(ctx); err != nil { if err := l.Wait(ctx); err != nil {
// Send an S3 compatible error, SlowDown. // Send an S3 compatible error, SlowDown.
writeErrorResponse(w, ErrSlowDown, r.URL) writeErrorResponse(w, ErrSlowDown, r.URL, guessIsBrowserReq(r))
return return
} }
l.handler.ServeHTTP(w, r) l.handler.ServeHTTP(w, r)
} }
// requestIDHeaderHandler sets x-amz-request-id header. // customHeaderHandler sets x-amz-request-id, x-minio-deployment-id header.
// Previously, this value was set right before a response // Previously, this value was set right before a response was sent to
// was sent to the client.So, logger and Error response XML // the client. So, logger and Error response XML were not using this
// were not using this value. // value. This is set here so that this header can be logged as
// This is set here so that this header can be logged as // part of the log entry, Error response XML and auditing.
// part of the log entry and Error response XML. type customHeaderHandler struct {
type requestIDHeaderHandler struct {
handler http.Handler handler http.Handler
} }
func addrequestIDHeader(h http.Handler) http.Handler { func addCustomHeaders(h http.Handler) http.Handler {
return requestIDHeaderHandler{handler: h} return customHeaderHandler{handler: h}
} }
func (s requestIDHeaderHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s customHeaderHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Set unique request ID for each response. // Set custom headers such as x-amz-request-id and x-minio-deployment-id
// for each request.
w.Header().Set(responseRequestIDKey, mustGetRequestID(UTCNow())) w.Header().Set(responseRequestIDKey, mustGetRequestID(UTCNow()))
s.handler.ServeHTTP(w, r) w.Header().Set(responseDeploymentIDKey, globalDeploymentID)
s.handler.ServeHTTP(logger.NewResponseWriter(w), r)
} }
type securityHeaderHandler struct { type securityHeaderHandler struct {
@@ -767,7 +771,7 @@ type criticalErrorHandler struct{ handler http.Handler }
func (h criticalErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h criticalErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer func() { defer func() {
if err := recover(); err == logger.ErrCritical { // handle if err := recover(); err == logger.ErrCritical { // handle
writeErrorResponse(w, ErrInternalError, r.URL) writeErrorResponse(w, ErrInternalError, r.URL, guessIsBrowserReq(r))
} else if err != nil { } else if err != nil {
panic(err) // forward other panic calls panic(err) // forward other panic calls
} }
@@ -786,7 +790,7 @@ func (h sseTLSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodHead { if r.Method == http.MethodHead {
writeErrorResponseHeadersOnly(w, ErrInsecureSSECustomerRequest) writeErrorResponseHeadersOnly(w, ErrInsecureSSECustomerRequest)
} else { } else {
writeErrorResponse(w, ErrInsecureSSECustomerRequest, r.URL) writeErrorResponse(w, ErrInsecureSSECustomerRequest, r.URL, guessIsBrowserReq(r))
} }
return return
} }
+7
View File
@@ -78,9 +78,16 @@ func TestGuessIsRPC(t *testing.T) {
if guessIsRPCReq(nil) { if guessIsRPCReq(nil) {
t.Fatal("Unexpected return for nil request") t.Fatal("Unexpected return for nil request")
} }
u, err := url.Parse("http://localhost:9000/minio/lock")
if err != nil {
t.Fatal(err)
}
r := &http.Request{ r := &http.Request{
Proto: "HTTP/1.0", Proto: "HTTP/1.0",
Method: http.MethodPost, Method: http.MethodPost,
URL: u,
} }
if !guessIsRPCReq(r) { if !guessIsRPCReq(r) {
t.Fatal("Test shouldn't fail for a possible net/rpc request.") t.Fatal("Test shouldn't fail for a possible net/rpc request.")
+3
View File
@@ -255,6 +255,9 @@ var (
// OPA policy system. // OPA policy system.
globalPolicyOPA *iampolicy.Opa globalPolicyOPA *iampolicy.Opa
// Deployment ID - unique per deployment
globalDeploymentID string
// Add new variable global values here. // Add new variable global values here.
) )
+8 -6
View File
@@ -28,6 +28,7 @@ import (
"strings" "strings"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/auth"
"github.com/minio/minio/pkg/handlers" "github.com/minio/minio/pkg/handlers"
httptracer "github.com/minio/minio/pkg/handlers" httptracer "github.com/minio/minio/pkg/handlers"
) )
@@ -176,13 +177,13 @@ func getRedirectPostRawQuery(objInfo ObjectInfo) string {
return redirectValues.Encode() return redirectValues.Encode()
} }
// Returns access key in the request Authorization header. // Returns access credentials in the request Authorization header.
func getReqAccessKey(r *http.Request, region string) (accessKey string) { func getReqAccessCred(r *http.Request, region string) (cred auth.Credentials) {
cred, _, _ := getReqAccessKeyV4(r, region) cred, _, _ = getReqAccessKeyV4(r, region)
if cred.AccessKey == "" { if cred.AccessKey == "" {
cred, _, _ = getReqAccessKeyV2(r) cred, _, _ = getReqAccessKeyV2(r)
} }
return cred.AccessKey return cred
} }
// Extract request params to be sent with event notifiation. // Extract request params to be sent with event notifiation.
@@ -192,10 +193,11 @@ func extractReqParams(r *http.Request) map[string]string {
} }
region := globalServerConfig.GetRegion() region := globalServerConfig.GetRegion()
cred := getReqAccessCred(r, region)
// Success. // Success.
return map[string]string{ return map[string]string{
"region": region, "region": region,
"accessKey": getReqAccessKey(r, region), "accessKey": cred.AccessKey,
"sourceIPAddress": handlers.GetSourceIP(r), "sourceIPAddress": handlers.GetSourceIP(r),
// Add more fields here. // Add more fields here.
} }
@@ -351,6 +353,6 @@ func getResource(path string, host string, domain string) (string, error) {
// If none of the http routes match respond with MethodNotAllowed // If none of the http routes match respond with MethodNotAllowed
func notFoundHandler(w http.ResponseWriter, r *http.Request) { func notFoundHandler(w http.ResponseWriter, r *http.Request) {
writeErrorResponse(w, ErrMethodNotAllowed, r.URL) writeErrorResponse(w, ErrMethodNotAllowed, r.URL, guessIsBrowserReq(r))
return return
} }
+1 -1
View File
@@ -1,4 +1,4 @@
// +build windows plan9 // +build windows plan9 solaris
/* /*
* Minio Cloud Storage, (C) 2018 Minio, Inc. * Minio Cloud Storage, (C) 2018 Minio, Inc.
-97
View File
@@ -1,97 +0,0 @@
/*
* Minio Cloud Storage, (C) 2018 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 logger
import (
"context"
"net/http"
"strings"
"time"
)
// Represents the current version of audit log structure.
const auditLogVersion = "1"
// AuditEntry - audit entry logs.
type AuditEntry struct {
Version string `json:"version"`
DeploymentID string `json:"deploymentid,omitempty"`
Time string `json:"time"`
API *api `json:"api,omitempty"`
RemoteHost string `json:"remotehost,omitempty"`
RequestID string `json:"requestID,omitempty"`
UserAgent string `json:"userAgent,omitempty"`
ReqQuery map[string]string `json:"requestQuery,omitempty"`
ReqHeader map[string]string `json:"requestHeader,omitempty"`
RespHeader map[string]string `json:"responseHeader,omitempty"`
}
// AuditTargets is the list of enabled audit loggers
var AuditTargets = []LoggingTarget{}
// AddAuditTarget adds a new audit logger target to the
// list of enabled loggers
func AddAuditTarget(t LoggingTarget) {
AuditTargets = append(AuditTargets, t)
}
// AuditLog - logs audit logs to all targets.
func AuditLog(ctx context.Context, w http.ResponseWriter, r *http.Request) {
if Disable {
return
}
req := GetReqInfo(ctx)
if req == nil {
req = &ReqInfo{API: "SYSTEM"}
}
reqQuery := make(map[string]string)
for k, v := range r.URL.Query() {
reqQuery[k] = strings.Join(v, ",")
}
reqHeader := make(map[string]string)
for k, v := range r.Header {
reqHeader[k] = strings.Join(v, ",")
}
respHeader := make(map[string]string)
for k, v := range w.Header() {
respHeader[k] = strings.Join(v, ",")
}
// Send audit logs only to http targets.
for _, t := range AuditTargets {
t.send(AuditEntry{
Version: auditLogVersion,
DeploymentID: deploymentID,
RemoteHost: req.RemoteHost,
RequestID: req.RequestID,
UserAgent: req.UserAgent,
Time: time.Now().UTC().Format(time.RFC3339Nano),
API: &api{
Name: req.API,
Args: &args{
Bucket: req.BucketName,
Object: req.ObjectName,
},
},
ReqQuery: reqQuery,
ReqHeader: reqHeader,
RespHeader: respHeader,
})
}
}
+68
View File
@@ -0,0 +1,68 @@
/*
* Minio Cloud Storage, (C) 2018 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 logger
import (
"net/http"
"github.com/minio/minio/cmd/logger/message/audit"
)
// ResponseWriter - is a wrapper to trap the http response status code.
type ResponseWriter struct {
http.ResponseWriter
statusCode int
}
// NewResponseWriter - returns a wrapped response writer to trap
// http status codes for auditiing purposes.
func NewResponseWriter(w http.ResponseWriter) *ResponseWriter {
return &ResponseWriter{w, http.StatusOK}
}
// WriteHeader - writes http status code
func (lrw *ResponseWriter) WriteHeader(code int) {
lrw.statusCode = code
lrw.ResponseWriter.WriteHeader(code)
}
// Flush - Calls the underlying Flush.
func (lrw *ResponseWriter) Flush() {
lrw.ResponseWriter.(http.Flusher).Flush()
}
// AuditTargets is the list of enabled audit loggers
var AuditTargets = []Target{}
// AddAuditTarget adds a new audit logger target to the
// list of enabled loggers
func AddAuditTarget(t Target) {
AuditTargets = append(AuditTargets, t)
}
// AuditLog - logs audit logs to all audit targets.
func AuditLog(w http.ResponseWriter, r *http.Request, api string, reqClaims map[string]interface{}) {
var statusCode int
lrw, ok := w.(*ResponseWriter)
if ok {
statusCode = lrw.statusCode
}
// Send audit logs only to http targets.
for _, t := range AuditTargets {
_ = t.Send(audit.ToEntry(w, r, api, statusCode, reqClaims))
}
}
+150 -69
View File
@@ -19,87 +19,168 @@ package logger
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os"
"strings" "strings"
"time" "time"
c "github.com/minio/mc/pkg/console"
"github.com/minio/minio/cmd/logger/message/log"
) )
// ConsoleTarget implements loggerTarget to send log // Console interface describes the methods that need to be implemented to satisfy the interface requirements.
// in plain or json format to the standard output. type Console interface {
type ConsoleTarget struct{} json(msg string, args ...interface{})
quiet(msg string, args ...interface{})
pretty(msg string, args ...interface{})
}
func (c *ConsoleTarget) send(e interface{}) error { func consoleLog(console Console, msg string, args ...interface{}) {
entry, ok := e.(logEntry) switch {
if !ok { case jsonFlag:
return fmt.Errorf("Uexpected log entry structure %#v", e) // Strip escape control characters from json message
msg = ansiRE.ReplaceAllLiteralString(msg, "")
console.json(msg, args...)
case quietFlag:
console.quiet(msg, args...)
default:
console.pretty(msg, args...)
} }
if jsonFlag { }
logJSON, err := json.Marshal(&entry)
if err != nil { // Fatal prints only fatal error message with no stack trace
return err // it will be called for input validation failures
func Fatal(err error, msg string, data ...interface{}) {
fatal(err, msg, data...)
}
func fatal(err error, msg string, data ...interface{}) {
var errMsg string
if msg != "" {
errMsg = errorFmtFunc(fmt.Sprintf(msg, data...), err, jsonFlag)
} else {
errMsg = err.Error()
}
consoleLog(fatalMessage, errMsg)
}
var fatalMessage fatalMsg
type fatalMsg struct {
}
func (f fatalMsg) json(msg string, args ...interface{}) {
logJSON, err := json.Marshal(&log.Entry{
Level: FatalLvl.String(),
Time: time.Now().UTC().Format(time.RFC3339Nano),
Trace: &log.Trace{Message: fmt.Sprintf(msg, args...), Source: []string{getSource(6)}},
})
if err != nil {
panic(err)
}
fmt.Println(string(logJSON))
os.Exit(1)
}
func (f fatalMsg) quiet(msg string, args ...interface{}) {
f.pretty(msg, args...)
}
var (
logTag = "ERROR"
logBanner = ColorBgRed(ColorFgWhite(ColorBold(logTag))) + " "
emptyBanner = ColorBgRed(strings.Repeat(" ", len(logTag))) + " "
bannerWidth = len(logTag) + 1
)
func (f fatalMsg) pretty(msg string, args ...interface{}) {
// Build the passed error message
errMsg := fmt.Sprintf(msg, args...)
tagPrinted := false
// Print the error message: the following code takes care
// of splitting error text and always pretty printing the
// red banner along with the error message. Since the error
// message itself contains some colored text, we needed
// to use some ANSI control escapes to cursor color state
// and freely move in the screen.
for _, line := range strings.Split(errMsg, "\n") {
if len(line) == 0 {
// No more text to print, just quit.
break
} }
fmt.Println(string(logJSON))
return nil
}
trace := make([]string, len(entry.Trace.Source)) for {
// Save the attributes of the current cursor helps
// Add a sequence number and formatting for each stack trace // us save the text color of the passed error message
// No formatting is required for the first entry ansiSaveAttributes()
for i, element := range entry.Trace.Source { // Print banner with or without the log tag
trace[i] = fmt.Sprintf("%8v: %s", i+1, element) if !tagPrinted {
} fmt.Print(logBanner)
tagPrinted = true
tagString := "" } else {
for key, value := range entry.Trace.Variables { fmt.Print(emptyBanner)
if value != "" {
if tagString != "" {
tagString += ", "
} }
tagString += key + "=" + value // Restore the text color of the error message
ansiRestoreAttributes()
ansiMoveRight(bannerWidth)
// Continue error message printing
fmt.Println(line)
break
} }
} }
apiString := "API: " + entry.API.Name + "(" // Exit because this is a fatal error message
if entry.API.Args != nil && entry.API.Args.Bucket != "" { os.Exit(1)
apiString = apiString + "bucket=" + entry.API.Args.Bucket
}
if entry.API.Args != nil && entry.API.Args.Object != "" {
apiString = apiString + ", object=" + entry.API.Args.Object
}
apiString += ")"
timeString := "Time: " + time.Now().Format(loggerTimeFormat)
var requestID string
if entry.RequestID != "" {
requestID = "\nRequestID: " + entry.RequestID
}
var remoteHost string
if entry.RemoteHost != "" {
remoteHost = "\nRemoteHost: " + entry.RemoteHost
}
var userAgent string
if entry.UserAgent != "" {
userAgent = "\nUserAgent: " + entry.UserAgent
}
if len(entry.Trace.Variables) > 0 {
tagString = "\n " + tagString
}
var msg = colorFgRed(colorBold(entry.Trace.Message))
var output = fmt.Sprintf("\n%s\n%s%s%s%s\nError: %s%s\n%s",
apiString, timeString, requestID, remoteHost, userAgent,
msg, tagString, strings.Join(trace, "\n"))
fmt.Println(output)
return nil
} }
// NewConsole initializes a new logger target type infoMsg struct{}
// which prints log directly in the standard
// output. var info infoMsg
func NewConsole() LoggingTarget {
return &ConsoleTarget{} func (i infoMsg) json(msg string, args ...interface{}) {
logJSON, err := json.Marshal(&log.Entry{
Level: InformationLvl.String(),
Message: fmt.Sprintf(msg, args...),
Time: time.Now().UTC().Format(time.RFC3339Nano),
})
if err != nil {
panic(err)
}
fmt.Println(string(logJSON))
}
func (i infoMsg) quiet(msg string, args ...interface{}) {
i.pretty(msg, args...)
}
func (i infoMsg) pretty(msg string, args ...interface{}) {
c.Printf(msg, args...)
}
// Info :
func Info(msg string, data ...interface{}) {
consoleLog(info, msg+"\n", data...)
}
var startupMessage startUpMsg
type startUpMsg struct {
}
func (s startUpMsg) json(msg string, args ...interface{}) {
}
func (s startUpMsg) quiet(msg string, args ...interface{}) {
}
func (s startUpMsg) pretty(msg string, args ...interface{}) {
c.Printf(msg, args...)
}
// StartupMessage :
func StartupMessage(msg string, data ...interface{}) {
consoleLog(startupMessage, msg+"\n", data...)
} }
+46 -209
View File
@@ -18,23 +18,20 @@ package logger
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"go/build" "go/build"
"os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings" "strings"
"time" "time"
c "github.com/minio/mc/pkg/console" "github.com/minio/minio-go/pkg/set"
"github.com/minio/minio/cmd/logger/message/log"
) )
// Disable disables all logging, false by default. (used for "go test") // Disable disables all logging, false by default. (used for "go test")
var Disable = false var Disable = false
var trimStrings []string
// Level type // Level type
type Level int8 type Level int8
@@ -45,7 +42,10 @@ const (
FatalLvl FatalLvl
) )
const loggerTimeFormat string = "15:04:05 MST 01/02/2006" var trimStrings []string
// TimeFormat - logging time format.
const TimeFormat string = "15:04:05 MST 01/02/2006"
// List of error strings to be ignored by LogIf // List of error strings to be ignored by LogIf
const ( const (
@@ -91,79 +91,33 @@ func (level Level) String() string {
return lvlStr return lvlStr
} }
// Console interface describes the methods that needs to be implemented to satisfy the interface requirements. // quietFlag: Hide startup messages if enabled
type Console interface {
json(msg string, args ...interface{})
quiet(msg string, args ...interface{})
pretty(msg string, args ...interface{})
}
func consoleLog(console Console, msg string, args ...interface{}) {
switch {
case jsonFlag:
// Strip escape control characters from json message
msg = ansiRE.ReplaceAllLiteralString(msg, "")
console.json(msg, args...)
case quiet:
console.quiet(msg, args...)
default:
console.pretty(msg, args...)
}
}
type traceEntry struct {
Message string `json:"message,omitempty"`
Source []string `json:"source,omitempty"`
Variables map[string]string `json:"variables,omitempty"`
}
type args struct {
Bucket string `json:"bucket,omitempty"`
Object string `json:"object,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
type api struct {
Name string `json:"name,omitempty"`
Args *args `json:"args,omitempty"`
}
type logEntry struct {
DeploymentID string `json:"deploymentid,omitempty"`
Level string `json:"level"`
Time string `json:"time"`
API *api `json:"api,omitempty"`
RemoteHost string `json:"remotehost,omitempty"`
RequestID string `json:"requestID,omitempty"`
UserAgent string `json:"userAgent,omitempty"`
Message string `json:"message,omitempty"`
Trace *traceEntry `json:"error,omitempty"`
}
// quiet: Hide startup messages if enabled
// jsonFlag: Display in JSON format, if enabled // jsonFlag: Display in JSON format, if enabled
var ( var (
quiet, jsonFlag bool quietFlag, jsonFlag bool
// Custom function to format error // Custom function to format error
errorFmtFunc func(string, error, bool) string errorFmtFunc func(string, error, bool) string
deploymentID string
) )
// SetDeploymentID - Used to set the deployment ID, in XL and FS mode
func SetDeploymentID(id string) {
deploymentID = id
}
// EnableQuiet - turns quiet option on. // EnableQuiet - turns quiet option on.
func EnableQuiet() { func EnableQuiet() {
quiet = true quietFlag = true
} }
// EnableJSON - outputs logs in json format. // EnableJSON - outputs logs in json format.
func EnableJSON() { func EnableJSON() {
jsonFlag = true jsonFlag = true
quiet = true quietFlag = true
}
// IsJSON - returns true if jsonFlag is true
func IsJSON() bool {
return jsonFlag
}
// IsQuiet - returns true if quietFlag is true
func IsQuiet() bool {
return quietFlag
} }
// RegisterUIError registers the specified rendering function. This latter // RegisterUIError registers the specified rendering function. This latter
@@ -172,6 +126,17 @@ func RegisterUIError(f func(string, error, bool) string) {
errorFmtFunc = f errorFmtFunc = f
} }
// Remove any duplicates and return unique entries.
func uniqueEntries(paths []string) []string {
m := make(set.StringSet)
for _, p := range paths {
if !m.Contains(p) {
m.Add(p)
}
}
return m.ToSlice()
}
// Init sets the trimStrings to possible GOPATHs // Init sets the trimStrings to possible GOPATHs
// and GOROOT directories. Also append github.com/minio/minio // and GOROOT directories. Also append github.com/minio/minio
// This is done to clean up the filename, when stack trace is // This is done to clean up the filename, when stack trace is
@@ -326,20 +291,30 @@ func logIf(ctx context.Context, err error) {
// Get the cause for the Error // Get the cause for the Error
message := err.Error() message := err.Error()
entry := logEntry{ entry := log.Entry{
DeploymentID: deploymentID, DeploymentID: req.DeploymentID,
Level: ErrorLvl.String(), Level: ErrorLvl.String(),
RemoteHost: req.RemoteHost, RemoteHost: req.RemoteHost,
RequestID: req.RequestID, RequestID: req.RequestID,
UserAgent: req.UserAgent, UserAgent: req.UserAgent,
Time: time.Now().UTC().Format(time.RFC3339Nano), Time: time.Now().UTC().Format(time.RFC3339Nano),
API: &api{Name: API, Args: &args{Bucket: req.BucketName, Object: req.ObjectName}}, API: &log.API{
Trace: &traceEntry{Message: message, Source: trace, Variables: tags}, Name: API,
Args: &log.Args{
Bucket: req.BucketName,
Object: req.ObjectName,
},
},
Trace: &log.Trace{
Message: message,
Source: trace,
Variables: tags,
},
} }
// Iterate over all logger targets to send the log entry // Iterate over all logger targets to send the log entry
for _, t := range Targets { for _, t := range Targets {
t.send(entry) t.Send(entry)
} }
} }
@@ -362,141 +337,3 @@ func FatalIf(err error, msg string, data ...interface{}) {
} }
fatal(err, msg, data...) fatal(err, msg, data...)
} }
// Fatal prints only fatal error message without no stack trace
// it will be called for input validation failures
func Fatal(err error, msg string, data ...interface{}) {
fatal(err, msg, data...)
}
func fatal(err error, msg string, data ...interface{}) {
var errMsg string
if msg != "" {
errMsg = errorFmtFunc(fmt.Sprintf(msg, data...), err, jsonFlag)
} else {
errMsg = err.Error()
}
consoleLog(fatalMessage, errMsg)
}
var fatalMessage fatalMsg
type fatalMsg struct {
}
func (f fatalMsg) json(msg string, args ...interface{}) {
logJSON, err := json.Marshal(&logEntry{
Level: FatalLvl.String(),
Time: time.Now().UTC().Format(time.RFC3339Nano),
Trace: &traceEntry{Message: fmt.Sprintf(msg, args...), Source: []string{getSource(6)}},
})
if err != nil {
panic(err)
}
fmt.Println(string(logJSON))
os.Exit(1)
}
func (f fatalMsg) quiet(msg string, args ...interface{}) {
f.pretty(msg, args...)
}
var (
logTag = "ERROR"
logBanner = colorBgRed(colorFgWhite(colorBold(logTag))) + " "
emptyBanner = colorBgRed(strings.Repeat(" ", len(logTag))) + " "
bannerWidth = len(logTag) + 1
)
func (f fatalMsg) pretty(msg string, args ...interface{}) {
// Build the passed error message
errMsg := fmt.Sprintf(msg, args...)
tagPrinted := false
// Print the error message: the following code takes care
// of splitting error text and always pretty printing the
// red banner along with the error message. Since the error
// message itself contains some colored text, we needed
// to use some ANSI control escapes to cursor color state
// and freely move in the screen.
for _, line := range strings.Split(errMsg, "\n") {
if len(line) == 0 {
// No more text to print, just quit.
break
}
for {
// Save the attributes of the current cursor helps
// us save the text color of the passed error message
ansiSaveAttributes()
// Print banner with or without the log tag
if !tagPrinted {
fmt.Print(logBanner)
tagPrinted = true
} else {
fmt.Print(emptyBanner)
}
// Restore the text color of the error message
ansiRestoreAttributes()
ansiMoveRight(bannerWidth)
// Continue error message printing
fmt.Println(line)
break
}
}
// Exit because this is a fatal error message
os.Exit(1)
}
type infoMsg struct{}
var info infoMsg
func (i infoMsg) json(msg string, args ...interface{}) {
logJSON, err := json.Marshal(&logEntry{
Level: InformationLvl.String(),
Message: fmt.Sprintf(msg, args...),
Time: time.Now().UTC().Format(time.RFC3339Nano),
})
if err != nil {
panic(err)
}
fmt.Println(string(logJSON))
}
func (i infoMsg) quiet(msg string, args ...interface{}) {
i.pretty(msg, args...)
}
func (i infoMsg) pretty(msg string, args ...interface{}) {
c.Printf(msg, args...)
}
// Info :
func Info(msg string, data ...interface{}) {
consoleLog(info, msg+"\n", data...)
}
var startupMessage startUpMsg
type startUpMsg struct {
}
func (s startUpMsg) json(msg string, args ...interface{}) {
}
func (s startUpMsg) quiet(msg string, args ...interface{}) {
}
func (s startUpMsg) pretty(msg string, args ...interface{}) {
c.Printf(msg, args...)
}
// StartupMessage :
func StartupMessage(msg string, data ...interface{}) {
consoleLog(startupMessage, msg+"\n", data...)
}
+92
View File
@@ -0,0 +1,92 @@
/*
* Minio Cloud Storage, (C) 2018 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 audit
import (
"net/http"
"strings"
"time"
"github.com/gorilla/mux"
"github.com/minio/minio/pkg/handlers"
)
// Version - represents the current version of audit log structure.
const Version = "1"
// Entry - audit entry logs.
type Entry struct {
Version string `json:"version"`
DeploymentID string `json:"deploymentid,omitempty"`
Time string `json:"time"`
API struct {
Name string `json:"name,omitempty"`
Bucket string `json:"bucket,omitempty"`
Object string `json:"object,omitempty"`
Status string `json:"status,omitempty"`
StatusCode int `json:"statusCode,omitempty"`
} `json:"api"`
RemoteHost string `json:"remotehost,omitempty"`
RequestID string `json:"requestID,omitempty"`
UserAgent string `json:"userAgent,omitempty"`
ReqClaims map[string]interface{} `json:"requestClaims,omitempty"`
ReqQuery map[string]string `json:"requestQuery,omitempty"`
ReqHeader map[string]string `json:"requestHeader,omitempty"`
RespHeader map[string]string `json:"responseHeader,omitempty"`
}
// ToEntry - constructs an audit entry object.
func ToEntry(w http.ResponseWriter, r *http.Request, api string, statusCode int, reqClaims map[string]interface{}) Entry {
vars := mux.Vars(r)
bucket := vars["bucket"]
object := vars["object"]
reqQuery := make(map[string]string)
for k, v := range r.URL.Query() {
reqQuery[k] = strings.Join(v, ",")
}
reqHeader := make(map[string]string)
for k, v := range r.Header {
reqHeader[k] = strings.Join(v, ",")
}
respHeader := make(map[string]string)
for k, v := range w.Header() {
respHeader[k] = strings.Join(v, ",")
}
respHeader["Etag"] = strings.Trim(respHeader["Etag"], `"`)
entry := Entry{
Version: Version,
DeploymentID: w.Header().Get("x-minio-deployment-id"),
RemoteHost: handlers.GetSourceIP(r),
RequestID: w.Header().Get("x-amz-request-id"),
UserAgent: r.UserAgent(),
Time: time.Now().UTC().Format(time.RFC3339Nano),
ReqQuery: reqQuery,
ReqHeader: reqHeader,
ReqClaims: reqClaims,
RespHeader: respHeader,
}
entry.API.Name = api
entry.API.Bucket = bucket
entry.API.Object = object
entry.API.Status = http.StatusText(statusCode)
entry.API.StatusCode = statusCode
return entry
}
+50
View File
@@ -0,0 +1,50 @@
/*
* Minio Cloud Storage, (C) 2018 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 log
// Args - defines the arguments for the API.
type Args struct {
Bucket string `json:"bucket,omitempty"`
Object string `json:"object,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
// Trace - defines the trace.
type Trace struct {
Message string `json:"message,omitempty"`
Source []string `json:"source,omitempty"`
Variables map[string]string `json:"variables,omitempty"`
}
// API - defines the api type and its args.
type API struct {
Name string `json:"name,omitempty"`
Args *Args `json:"args,omitempty"`
}
// Entry - defines fields and values of each log entry.
type Entry struct {
DeploymentID string `json:"deploymentid,omitempty"`
Level string `json:"level"`
Time string `json:"time"`
API *API `json:"api,omitempty"`
RemoteHost string `json:"remotehost,omitempty"`
RequestID string `json:"requestID,omitempty"`
UserAgent string `json:"userAgent,omitempty"`
Message string `json:"message,omitempty"`
Trace *Trace `json:"error,omitempty"`
}
+10 -8
View File
@@ -35,22 +35,24 @@ type KeyVal struct {
// ReqInfo stores the request info. // ReqInfo stores the request info.
type ReqInfo struct { type ReqInfo struct {
RemoteHost string // Client Host/IP RemoteHost string // Client Host/IP
UserAgent string // User Agent UserAgent string // User Agent
RequestID string // x-amz-request-id DeploymentID string // x-minio-deployment-id
API string // API name - GetObject PutObject NewMultipartUpload etc. RequestID string // x-amz-request-id
BucketName string // Bucket name API string // API name - GetObject PutObject NewMultipartUpload etc.
ObjectName string // Object name BucketName string // Bucket name
tags []KeyVal // Any additional info not accommodated by above fields ObjectName string // Object name
tags []KeyVal // Any additional info not accommodated by above fields
sync.RWMutex sync.RWMutex
} }
// NewReqInfo : // NewReqInfo :
func NewReqInfo(remoteHost, userAgent, requestID, api, bucket, object string) *ReqInfo { func NewReqInfo(remoteHost, userAgent, deploymentID, requestID, api, bucket, object string) *ReqInfo {
req := ReqInfo{} req := ReqInfo{}
req.RemoteHost = remoteHost req.RemoteHost = remoteHost
req.UserAgent = userAgent req.UserAgent = userAgent
req.API = api req.API = api
req.DeploymentID = deploymentID
req.RequestID = requestID req.RequestID = requestID
req.BucketName = bucket req.BucketName = bucket
req.ObjectName = object req.ObjectName = object
+109
View File
@@ -0,0 +1,109 @@
/*
* Minio Cloud Storage, (C) 2018 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 console
import (
"encoding/json"
"fmt"
"strings"
"time"
"github.com/minio/minio/cmd/logger"
"github.com/minio/minio/cmd/logger/message/log"
)
// Target implements loggerTarget to send log
// in plain or json format to the standard output.
type Target struct{}
// Send log message 'e' to console
func (c *Target) Send(e interface{}) error {
entry, ok := e.(log.Entry)
if !ok {
return fmt.Errorf("Uexpected log entry structure %#v", e)
}
if logger.IsJSON() {
logJSON, err := json.Marshal(&entry)
if err != nil {
return err
}
fmt.Println(string(logJSON))
return nil
}
trace := make([]string, len(entry.Trace.Source))
// Add a sequence number and formatting for each stack trace
// No formatting is required for the first entry
for i, element := range entry.Trace.Source {
trace[i] = fmt.Sprintf("%8v: %s", i+1, element)
}
tagString := ""
for key, value := range entry.Trace.Variables {
if value != "" {
if tagString != "" {
tagString += ", "
}
tagString += key + "=" + value
}
}
apiString := "API: " + entry.API.Name + "("
if entry.API.Args != nil && entry.API.Args.Bucket != "" {
apiString = apiString + "bucket=" + entry.API.Args.Bucket
}
if entry.API.Args != nil && entry.API.Args.Object != "" {
apiString = apiString + ", object=" + entry.API.Args.Object
}
apiString += ")"
timeString := "Time: " + time.Now().Format(logger.TimeFormat)
var requestID string
if entry.RequestID != "" {
requestID = "\nRequestID: " + entry.RequestID
}
var remoteHost string
if entry.RemoteHost != "" {
remoteHost = "\nRemoteHost: " + entry.RemoteHost
}
var userAgent string
if entry.UserAgent != "" {
userAgent = "\nUserAgent: " + entry.UserAgent
}
if len(entry.Trace.Variables) > 0 {
tagString = "\n " + tagString
}
var msg = logger.ColorFgRed(logger.ColorBold(entry.Trace.Message))
var output = fmt.Sprintf("\n%s\n%s%s%s%s\nError: %s%s\n%s",
apiString, timeString, requestID, remoteHost, userAgent,
msg, tagString, strings.Join(trace, "\n"))
fmt.Println(output)
return nil
}
// New initializes a new logger target
// which prints log directly in the standard
// output.
func New() *Target {
return &Target{}
}
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package logger package http
import ( import (
"bytes" "bytes"
@@ -22,24 +22,24 @@ import (
"errors" "errors"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" gohttp "net/http"
) )
// HTTPTarget implements loggerTarget and sends the json // Target implements logger.Target and sends the json
// format of a log entry to the configured http endpoint. // format of a log entry to the configured http endpoint.
// An internal buffer of logs is maintained but when the // An internal buffer of logs is maintained but when the
// buffer is full, new logs are just ignored and an error // buffer is full, new logs are just ignored and an error
// is returned to the caller. // is returned to the caller.
type HTTPTarget struct { type Target struct {
// Channel of log entries // Channel of log entries
logCh chan interface{} logCh chan interface{}
// HTTP(s) endpoint // HTTP(s) endpoint
endpoint string endpoint string
client http.Client client gohttp.Client
} }
func (h *HTTPTarget) startHTTPLogger() { func (h *Target) startHTTPLogger() {
// Create a routine which sends json logs received // Create a routine which sends json logs received
// from an internal channel. // from an internal channel.
go func() { go func() {
@@ -49,7 +49,7 @@ func (h *HTTPTarget) startHTTPLogger() {
continue continue
} }
req, err := http.NewRequest("POST", h.endpoint, bytes.NewBuffer(logJSON)) req, err := gohttp.NewRequest("POST", h.endpoint, bytes.NewBuffer(logJSON))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
resp, err := h.client.Do(req) resp, err := h.client.Do(req)
@@ -65,12 +65,12 @@ func (h *HTTPTarget) startHTTPLogger() {
}() }()
} }
// NewHTTP initializes a new logger target which // New initializes a new logger target which
// sends log over http to the specified endpoint // sends log over http to the specified endpoint
func NewHTTP(endpoint string, transport *http.Transport) LoggingTarget { func New(endpoint string, transport *gohttp.Transport) *Target {
h := HTTPTarget{ h := Target{
endpoint: endpoint, endpoint: endpoint,
client: http.Client{ client: gohttp.Client{
Transport: transport, Transport: transport,
}, },
logCh: make(chan interface{}, 10000), logCh: make(chan interface{}, 10000),
@@ -80,7 +80,8 @@ func NewHTTP(endpoint string, transport *http.Transport) LoggingTarget {
return &h return &h
} }
func (h *HTTPTarget) send(entry interface{}) error { // Send log message 'e' to http target.
func (h *Target) Send(entry interface{}) error {
select { select {
case h.logCh <- entry: case h.logCh <- entry:
default: default:
+7 -7
View File
@@ -16,18 +16,18 @@
package logger package logger
// LoggingTarget is the entity that we will receive // Target is the entity that we will receive
// a single log entry and send it to the log target // a single log entry and Send it to the log target
// e.g. send the log to a http server // e.g. Send the log to a http server
type LoggingTarget interface { type Target interface {
send(entry interface{}) error Send(entry interface{}) error
} }
// Targets is the set of enabled loggers // Targets is the set of enabled loggers
var Targets = []LoggingTarget{} var Targets = []Target{}
// AddTarget adds a new logger target to the // AddTarget adds a new logger target to the
// list of enabled loggers // list of enabled loggers
func AddTarget(t LoggingTarget) { func AddTarget(t Target) {
Targets = append(Targets, t) Targets = append(Targets, t)
} }
+4 -16
View File
@@ -33,25 +33,25 @@ var (
return isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stderr.Fd()) return isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stderr.Fd())
} }
colorBold = func() func(a ...interface{}) string { ColorBold = func() func(a ...interface{}) string {
if isTerminal() { if isTerminal() {
return color.New(color.Bold).SprintFunc() return color.New(color.Bold).SprintFunc()
} }
return fmt.Sprint return fmt.Sprint
}() }()
colorFgRed = func() func(format string, a ...interface{}) string { ColorFgRed = func() func(format string, a ...interface{}) string {
if isTerminal() { if isTerminal() {
return color.New(color.FgRed).SprintfFunc() return color.New(color.FgRed).SprintfFunc()
} }
return fmt.Sprintf return fmt.Sprintf
}() }()
colorBgRed = func() func(format string, a ...interface{}) string { ColorBgRed = func() func(format string, a ...interface{}) string {
if isTerminal() { if isTerminal() {
return color.New(color.BgRed).SprintfFunc() return color.New(color.BgRed).SprintfFunc()
} }
return fmt.Sprintf return fmt.Sprintf
}() }()
colorFgWhite = func() func(format string, a ...interface{}) string { ColorFgWhite = func() func(format string, a ...interface{}) string {
if isTerminal() { if isTerminal() {
return color.New(color.FgWhite).SprintfFunc() return color.New(color.FgWhite).SprintfFunc()
} }
@@ -83,17 +83,5 @@ func ansiRestoreAttributes() {
if isTerminal() { if isTerminal() {
ansiEscape("8") ansiEscape("8")
} }
}
func uniqueEntries(paths []string) []string {
found := map[string]bool{}
unqiue := []string{}
for v := range paths {
if _, ok := found[paths[v]]; !ok {
found[paths[v]] = true
unqiue = append(unqiue, paths[v])
}
}
return unqiue
} }
+2
View File
@@ -108,6 +108,8 @@ type ObjectInfo struct {
// Implements writer and reader used by CopyObject API // Implements writer and reader used by CopyObject API
Writer io.WriteCloser `json:"-"` Writer io.WriteCloser `json:"-"`
Reader *hash.Reader `json:"-"` Reader *hash.Reader `json:"-"`
PutObjReader *PutObjReader `json:"-"`
metadataOnly bool metadataOnly bool
// Date and time when the object was last accessed. // Date and time when the object was last accessed.
+1 -1
View File
@@ -92,7 +92,7 @@ func testDeleteObject(obj ObjectLayer, instanceType string, t TestErrHandler) {
for _, object := range testCase.objectToUploads { for _, object := range testCase.objectToUploads {
md5Bytes := md5.Sum([]byte(object.content)) md5Bytes := md5.Sum([]byte(object.content))
_, err = obj.PutObject(context.Background(), testCase.bucketName, object.name, mustGetHashReader(t, bytes.NewBufferString(object.content), _, err = obj.PutObject(context.Background(), testCase.bucketName, object.name, mustGetPutObjReader(t, bytes.NewBufferString(object.content),
int64(len(object.content)), hex.EncodeToString(md5Bytes[:]), ""), nil, ObjectOptions{}) int64(len(object.content)), hex.EncodeToString(md5Bytes[:]), ""), nil, ObjectOptions{})
if err != nil { if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error()) t.Fatalf("%s : %s", instanceType, err.Error())
+3 -3
View File
@@ -75,7 +75,7 @@ func testGetObject(obj ObjectLayer, instanceType string, t TestErrHandler) {
// iterate through the above set of inputs and upkoad the object. // iterate through the above set of inputs and upkoad the object.
for i, input := range putObjectInputs { for i, input := range putObjectInputs {
// uploading the object. // uploading the object.
_, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetHashReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData["etag"], ""), input.metaData, ObjectOptions{}) _, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData["etag"], ""), input.metaData, ObjectOptions{})
// if object upload fails stop the test. // if object upload fails stop the test.
if err != nil { if err != nil {
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err) t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
@@ -220,7 +220,7 @@ func testGetObjectPermissionDenied(obj ObjectLayer, instanceType string, disks [
// iterate through the above set of inputs and upkoad the object. // iterate through the above set of inputs and upkoad the object.
for i, input := range putObjectInputs { for i, input := range putObjectInputs {
// uploading the object. // uploading the object.
_, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetHashReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData["etag"], ""), input.metaData, ObjectOptions{}) _, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData["etag"], ""), input.metaData, ObjectOptions{})
// if object upload fails stop the test. // if object upload fails stop the test.
if err != nil { if err != nil {
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err) t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
@@ -333,7 +333,7 @@ func testGetObjectDiskNotFound(obj ObjectLayer, instanceType string, disks []str
// iterate through the above set of inputs and upkoad the object. // iterate through the above set of inputs and upkoad the object.
for i, input := range putObjectInputs { for i, input := range putObjectInputs {
// uploading the object. // uploading the object.
_, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetHashReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData["etag"], ""), input.metaData, ObjectOptions{}) _, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData["etag"], ""), input.metaData, ObjectOptions{})
// if object upload fails stop the test. // if object upload fails stop the test.
if err != nil { if err != nil {
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err) t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
+2 -2
View File
@@ -35,13 +35,13 @@ func testGetObjectInfo(obj ObjectLayer, instanceType string, t TestErrHandler) {
t.Fatalf("%s : %s", instanceType, err.Error()) t.Fatalf("%s : %s", instanceType, err.Error())
} }
opts := ObjectOptions{} opts := ObjectOptions{}
_, err = obj.PutObject(context.Background(), "test-getobjectinfo", "Asia/asiapics.jpg", mustGetHashReader(t, bytes.NewBufferString("asiapics"), int64(len("asiapics")), "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "test-getobjectinfo", "Asia/asiapics.jpg", mustGetPutObjReader(t, bytes.NewBufferString("asiapics"), int64(len("asiapics")), "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error()) t.Fatalf("%s : %s", instanceType, err.Error())
} }
// Put an empty directory // Put an empty directory
_, err = obj.PutObject(context.Background(), "test-getobjectinfo", "Asia/empty-dir/", mustGetHashReader(t, bytes.NewBufferString(""), int64(len("")), "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "test-getobjectinfo", "Asia/empty-dir/", mustGetPutObjReader(t, bytes.NewBufferString(""), int64(len("")), "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error()) t.Fatalf("%s : %s", instanceType, err.Error())
} }
+4 -4
View File
@@ -22,7 +22,6 @@ import (
"net/http" "net/http"
"github.com/minio/minio-go/pkg/encrypt" "github.com/minio/minio-go/pkg/encrypt"
"github.com/minio/minio/pkg/hash"
"github.com/minio/minio/pkg/madmin" "github.com/minio/minio/pkg/madmin"
"github.com/minio/minio/pkg/policy" "github.com/minio/minio/pkg/policy"
) )
@@ -66,7 +65,7 @@ type ObjectLayer interface {
GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (reader *GetObjectReader, err error) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (reader *GetObjectReader, err error)
GetObject(ctx context.Context, bucket, object string, startOffset int64, length int64, writer io.Writer, etag string, opts ObjectOptions) (err error) GetObject(ctx context.Context, bucket, object string, startOffset int64, length int64, writer io.Writer, etag string, opts ObjectOptions) (err error)
GetObjectInfo(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error) GetObjectInfo(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error)
PutObject(ctx context.Context, bucket, object string, data *hash.Reader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, err error) PutObject(ctx context.Context, bucket, object string, data *PutObjReader, metadata map[string]string, opts ObjectOptions) (objInfo ObjectInfo, err error)
CopyObject(ctx context.Context, srcBucket, srcObject, destBucket, destObject string, srcInfo ObjectInfo, srcOpts, dstOpts ObjectOptions) (objInfo ObjectInfo, err error) CopyObject(ctx context.Context, srcBucket, srcObject, destBucket, destObject string, srcInfo ObjectInfo, srcOpts, dstOpts ObjectOptions) (objInfo ObjectInfo, err error)
DeleteObject(ctx context.Context, bucket, object string) error DeleteObject(ctx context.Context, bucket, object string) error
@@ -75,10 +74,10 @@ type ObjectLayer interface {
NewMultipartUpload(ctx context.Context, bucket, object string, metadata map[string]string, opts ObjectOptions) (uploadID string, err error) NewMultipartUpload(ctx context.Context, bucket, object string, metadata map[string]string, opts ObjectOptions) (uploadID string, err error)
CopyObjectPart(ctx context.Context, srcBucket, srcObject, destBucket, destObject string, uploadID string, partID int, CopyObjectPart(ctx context.Context, srcBucket, srcObject, destBucket, destObject string, uploadID string, partID int,
startOffset int64, length int64, srcInfo ObjectInfo, srcOpts, dstOpts ObjectOptions) (info PartInfo, err error) startOffset int64, length int64, srcInfo ObjectInfo, srcOpts, dstOpts ObjectOptions) (info PartInfo, err error)
PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, data *hash.Reader, opts ObjectOptions) (info PartInfo, err error) PutObjectPart(ctx context.Context, bucket, object, uploadID string, partID int, data *PutObjReader, opts ObjectOptions) (info PartInfo, err error)
ListObjectParts(ctx context.Context, bucket, object, uploadID string, partNumberMarker int, maxParts int) (result ListPartsInfo, err error) ListObjectParts(ctx context.Context, bucket, object, uploadID string, partNumberMarker int, maxParts int) (result ListPartsInfo, err error)
AbortMultipartUpload(ctx context.Context, bucket, object, uploadID string) error AbortMultipartUpload(ctx context.Context, bucket, object, uploadID string) error
CompleteMultipartUpload(ctx context.Context, bucket, object, uploadID string, uploadedParts []CompletePart) (objInfo ObjectInfo, err error) CompleteMultipartUpload(ctx context.Context, bucket, object, uploadID string, uploadedParts []CompletePart, opts ObjectOptions) (objInfo ObjectInfo, err error)
// Healing operations. // Healing operations.
ReloadFormat(ctx context.Context, dryRun bool) error ReloadFormat(ctx context.Context, dryRun bool) error
@@ -95,6 +94,7 @@ type ObjectLayer interface {
// Supported operations check // Supported operations check
IsNotificationSupported() bool IsNotificationSupported() bool
IsListenBucketSupported() bool
IsEncryptionSupported() bool IsEncryptionSupported() bool
// Compression support check. // Compression support check.
+2 -2
View File
@@ -70,7 +70,7 @@ func testListObjects(obj ObjectLayer, instanceType string, t TestErrHandler) {
} }
for _, object := range testObjects { for _, object := range testObjects {
md5Bytes := md5.Sum([]byte(object.content)) md5Bytes := md5.Sum([]byte(object.content))
_, err = obj.PutObject(context.Background(), testBuckets[0], object.name, mustGetHashReader(t, bytes.NewBufferString(object.content), _, err = obj.PutObject(context.Background(), testBuckets[0], object.name, mustGetPutObjReader(t, bytes.NewBufferString(object.content),
int64(len(object.content)), hex.EncodeToString(md5Bytes[:]), ""), object.meta, ObjectOptions{}) int64(len(object.content)), hex.EncodeToString(md5Bytes[:]), ""), object.meta, ObjectOptions{})
if err != nil { if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error()) t.Fatalf("%s : %s", instanceType, err.Error())
@@ -617,7 +617,7 @@ func BenchmarkListObjects(b *testing.B) {
// Insert objects to be listed and benchmarked later. // Insert objects to be listed and benchmarked later.
for i := 0; i < 20000; i++ { for i := 0; i < 20000; i++ {
key := "obj" + strconv.Itoa(i) key := "obj" + strconv.Itoa(i)
_, err = obj.PutObject(context.Background(), bucket, key, mustGetHashReader(b, bytes.NewBufferString(key), int64(len(key)), "", ""), nil, ObjectOptions{}) _, err = obj.PutObject(context.Background(), bucket, key, mustGetPutObjReader(b, bytes.NewBufferString(key), int64(len(key)), "", ""), nil, ObjectOptions{})
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
+8 -8
View File
@@ -221,7 +221,7 @@ func testPutObjectPartDiskNotFound(obj ObjectLayer, instanceType string, disks [
sha256sum := "" sha256sum := ""
// Iterating over creatPartCases to generate multipart chunks. // Iterating over creatPartCases to generate multipart chunks.
for _, testCase := range createPartCases { for _, testCase := range createPartCases {
_, err = obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetHashReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, sha256sum), ObjectOptions{}) _, err = obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, sha256sum), ObjectOptions{})
if err != nil { if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error()) t.Fatalf("%s : %s", instanceType, err.Error())
} }
@@ -235,7 +235,7 @@ func testPutObjectPartDiskNotFound(obj ObjectLayer, instanceType string, disks [
// Object part upload should fail with quorum not available. // Object part upload should fail with quorum not available.
testCase := createPartCases[len(createPartCases)-1] testCase := createPartCases[len(createPartCases)-1]
_, err = obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetHashReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, sha256sum), ObjectOptions{}) _, err = obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, sha256sum), ObjectOptions{})
if err == nil { if err == nil {
t.Fatalf("Test %s: expected to fail but passed instead", instanceType) t.Fatalf("Test %s: expected to fail but passed instead", instanceType)
} }
@@ -354,7 +354,7 @@ func testObjectAPIPutObjectPart(obj ObjectLayer, instanceType string, t TestErrH
// Validate all the test cases. // Validate all the test cases.
for i, testCase := range testCases { for i, testCase := range testCases {
actualInfo, actualErr := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetHashReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, testCase.inputSHA256), opts) actualInfo, actualErr := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, testCase.inputSHA256), opts)
// All are test cases above are expected to fail. // All are test cases above are expected to fail.
if actualErr != nil && testCase.shouldPass { if actualErr != nil && testCase.shouldPass {
t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s.", i+1, instanceType, actualErr.Error()) t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s.", i+1, instanceType, actualErr.Error())
@@ -488,7 +488,7 @@ func testListMultipartUploads(obj ObjectLayer, instanceType string, t TestErrHan
sha256sum := "" sha256sum := ""
// Iterating over creatPartCases to generate multipart chunks. // Iterating over creatPartCases to generate multipart chunks.
for _, testCase := range createPartCases { for _, testCase := range createPartCases {
_, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetHashReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, sha256sum), opts) _, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, sha256sum), opts)
if err != nil { if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error()) t.Fatalf("%s : %s", instanceType, err.Error())
} }
@@ -1309,7 +1309,7 @@ func testListObjectPartsDiskNotFound(obj ObjectLayer, instanceType string, disks
sha256sum := "" sha256sum := ""
// Iterating over creatPartCases to generate multipart chunks. // Iterating over creatPartCases to generate multipart chunks.
for _, testCase := range createPartCases { for _, testCase := range createPartCases {
_, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetHashReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, sha256sum), opts) _, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, sha256sum), opts)
if err != nil { if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error()) t.Fatalf("%s : %s", instanceType, err.Error())
} }
@@ -1550,7 +1550,7 @@ func testListObjectParts(obj ObjectLayer, instanceType string, t TestErrHandler)
sha256sum := "" sha256sum := ""
// Iterating over creatPartCases to generate multipart chunks. // Iterating over creatPartCases to generate multipart chunks.
for _, testCase := range createPartCases { for _, testCase := range createPartCases {
_, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetHashReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, sha256sum), opts) _, err := obj.PutObjectPart(context.Background(), testCase.bucketName, testCase.objName, testCase.uploadID, testCase.PartID, mustGetPutObjReader(t, bytes.NewBufferString(testCase.inputReaderData), testCase.intputDataSize, testCase.inputMd5, sha256sum), opts)
if err != nil { if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error()) t.Fatalf("%s : %s", instanceType, err.Error())
} }
@@ -1801,7 +1801,7 @@ func testObjectCompleteMultipartUpload(obj ObjectLayer, instanceType string, t T
sha256sum := "" sha256sum := ""
// Iterating over creatPartCases to generate multipart chunks. // Iterating over creatPartCases to generate multipart chunks.
for _, part := range parts { for _, part := range parts {
_, err = obj.PutObjectPart(context.Background(), part.bucketName, part.objName, part.uploadID, part.PartID, mustGetHashReader(t, bytes.NewBufferString(part.inputReaderData), part.intputDataSize, part.inputMd5, sha256sum), opts) _, err = obj.PutObjectPart(context.Background(), part.bucketName, part.objName, part.uploadID, part.PartID, mustGetPutObjReader(t, bytes.NewBufferString(part.inputReaderData), part.intputDataSize, part.inputMd5, sha256sum), opts)
if err != nil { if err != nil {
t.Fatalf("%s : %s", instanceType, err) t.Fatalf("%s : %s", instanceType, err)
} }
@@ -1904,7 +1904,7 @@ func testObjectCompleteMultipartUpload(obj ObjectLayer, instanceType string, t T
} }
for i, testCase := range testCases { for i, testCase := range testCases {
actualResult, actualErr := obj.CompleteMultipartUpload(context.Background(), testCase.bucket, testCase.object, testCase.uploadID, testCase.parts) actualResult, actualErr := obj.CompleteMultipartUpload(context.Background(), testCase.bucket, testCase.object, testCase.uploadID, testCase.parts, ObjectOptions{})
if actualErr != nil && testCase.shouldPass { if actualErr != nil && testCase.shouldPass {
t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s", i+1, instanceType, actualErr) t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s", i+1, instanceType, actualErr)
} }
+7 -7
View File
@@ -172,7 +172,7 @@ func testObjectAPIPutObject(obj ObjectLayer, instanceType string, t TestErrHandl
} }
for i, testCase := range testCases { for i, testCase := range testCases {
objInfo, actualErr := obj.PutObject(context.Background(), testCase.bucketName, testCase.objName, mustGetHashReader(t, bytes.NewReader(testCase.inputData), testCase.intputDataSize, testCase.inputMeta["etag"], testCase.inputSHA256), testCase.inputMeta, ObjectOptions{}) objInfo, actualErr := obj.PutObject(context.Background(), testCase.bucketName, testCase.objName, mustGetPutObjReader(t, bytes.NewReader(testCase.inputData), testCase.intputDataSize, testCase.inputMeta["etag"], testCase.inputSHA256), testCase.inputMeta, ObjectOptions{})
if actualErr != nil && testCase.expectedError == nil { if actualErr != nil && testCase.expectedError == nil {
t.Errorf("Test %d: %s: Expected to pass, but failed with: error %s.", i+1, instanceType, actualErr.Error()) t.Errorf("Test %d: %s: Expected to pass, but failed with: error %s.", i+1, instanceType, actualErr.Error())
} }
@@ -245,7 +245,7 @@ func testObjectAPIPutObjectDiskNotFound(obj ObjectLayer, instanceType string, di
sha256sum := "" sha256sum := ""
for i, testCase := range testCases { for i, testCase := range testCases {
objInfo, actualErr := obj.PutObject(context.Background(), testCase.bucketName, testCase.objName, mustGetHashReader(t, bytes.NewReader(testCase.inputData), testCase.intputDataSize, testCase.inputMeta["etag"], sha256sum), testCase.inputMeta, ObjectOptions{}) objInfo, actualErr := obj.PutObject(context.Background(), testCase.bucketName, testCase.objName, mustGetPutObjReader(t, bytes.NewReader(testCase.inputData), testCase.intputDataSize, testCase.inputMeta["etag"], sha256sum), testCase.inputMeta, ObjectOptions{})
if actualErr != nil && testCase.shouldPass { if actualErr != nil && testCase.shouldPass {
t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s.", i+1, instanceType, actualErr.Error()) t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s.", i+1, instanceType, actualErr.Error())
} }
@@ -294,7 +294,7 @@ func testObjectAPIPutObjectDiskNotFound(obj ObjectLayer, instanceType string, di
InsufficientWriteQuorum{}, InsufficientWriteQuorum{},
} }
_, actualErr := obj.PutObject(context.Background(), testCase.bucketName, testCase.objName, mustGetHashReader(t, bytes.NewReader(testCase.inputData), testCase.intputDataSize, testCase.inputMeta["etag"], sha256sum), testCase.inputMeta, ObjectOptions{}) _, actualErr := obj.PutObject(context.Background(), testCase.bucketName, testCase.objName, mustGetPutObjReader(t, bytes.NewReader(testCase.inputData), testCase.intputDataSize, testCase.inputMeta["etag"], sha256sum), testCase.inputMeta, ObjectOptions{})
if actualErr != nil && testCase.shouldPass { if actualErr != nil && testCase.shouldPass {
t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s.", len(testCases)+1, instanceType, actualErr.Error()) t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s.", len(testCases)+1, instanceType, actualErr.Error())
} }
@@ -326,7 +326,7 @@ func testObjectAPIPutObjectStaleFiles(obj ObjectLayer, instanceType string, disk
data := []byte("hello, world") data := []byte("hello, world")
// Create object. // Create object.
_, err = obj.PutObject(context.Background(), bucket, object, mustGetHashReader(t, bytes.NewReader(data), int64(len(data)), "", ""), nil, ObjectOptions{}) _, err = obj.PutObject(context.Background(), bucket, object, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), nil, ObjectOptions{})
if err != nil { if err != nil {
// Failed to create object, abort. // Failed to create object, abort.
t.Fatalf("%s : %s", instanceType, err.Error()) t.Fatalf("%s : %s", instanceType, err.Error())
@@ -371,7 +371,7 @@ func testObjectAPIMultipartPutObjectStaleFiles(obj ObjectLayer, instanceType str
md5Writer.Write(fiveMBBytes) md5Writer.Write(fiveMBBytes)
etag1 := hex.EncodeToString(md5Writer.Sum(nil)) etag1 := hex.EncodeToString(md5Writer.Sum(nil))
sha256sum := "" sha256sum := ""
_, err = obj.PutObjectPart(context.Background(), bucket, object, uploadID, 1, mustGetHashReader(t, bytes.NewReader(fiveMBBytes), int64(len(fiveMBBytes)), etag1, sha256sum), opts) _, err = obj.PutObjectPart(context.Background(), bucket, object, uploadID, 1, mustGetPutObjReader(t, bytes.NewReader(fiveMBBytes), int64(len(fiveMBBytes)), etag1, sha256sum), opts)
if err != nil { if err != nil {
// Failed to upload object part, abort. // Failed to upload object part, abort.
t.Fatalf("%s : %s", instanceType, err.Error()) t.Fatalf("%s : %s", instanceType, err.Error())
@@ -382,7 +382,7 @@ func testObjectAPIMultipartPutObjectStaleFiles(obj ObjectLayer, instanceType str
md5Writer = md5.New() md5Writer = md5.New()
md5Writer.Write(data) md5Writer.Write(data)
etag2 := hex.EncodeToString(md5Writer.Sum(nil)) etag2 := hex.EncodeToString(md5Writer.Sum(nil))
_, err = obj.PutObjectPart(context.Background(), bucket, object, uploadID, 2, mustGetHashReader(t, bytes.NewReader(data), int64(len(data)), etag2, sha256sum), opts) _, err = obj.PutObjectPart(context.Background(), bucket, object, uploadID, 2, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), etag2, sha256sum), opts)
if err != nil { if err != nil {
// Failed to upload object part, abort. // Failed to upload object part, abort.
t.Fatalf("%s : %s", instanceType, err.Error()) t.Fatalf("%s : %s", instanceType, err.Error())
@@ -393,7 +393,7 @@ func testObjectAPIMultipartPutObjectStaleFiles(obj ObjectLayer, instanceType str
{ETag: etag1, PartNumber: 1}, {ETag: etag1, PartNumber: 1},
{ETag: etag2, PartNumber: 2}, {ETag: etag2, PartNumber: 2},
} }
_, err = obj.CompleteMultipartUpload(context.Background(), bucket, object, uploadID, parts) _, err = obj.CompleteMultipartUpload(context.Background(), bucket, object, uploadID, parts, ObjectOptions{})
if err != nil { if err != nil {
// Failed to complete multipart upload, abort. // Failed to complete multipart upload, abort.
t.Fatalf("%s : %s", instanceType, err.Error()) t.Fatalf("%s : %s", instanceType, err.Error())
+62
View File
@@ -17,6 +17,7 @@
package cmd package cmd
import ( import (
"bytes"
"context" "context"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
@@ -35,6 +36,7 @@ import (
"github.com/minio/minio/cmd/crypto" "github.com/minio/minio/cmd/crypto"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/dns" "github.com/minio/minio/pkg/dns"
"github.com/minio/minio/pkg/hash"
"github.com/minio/minio/pkg/ioutil" "github.com/minio/minio/pkg/ioutil"
"github.com/minio/minio/pkg/wildcard" "github.com/minio/minio/pkg/wildcard"
"github.com/skyrings/skyring-common/tools/uuid" "github.com/skyrings/skyring-common/tools/uuid"
@@ -597,3 +599,63 @@ func (g *GetObjectReader) Read(p []byte) (n int, err error) {
} }
return return
} }
//SealMD5CurrFn seals md5sum with object encryption key and returns sealed
// md5sum
type SealMD5CurrFn func([]byte) []byte
// PutObjReader is a type that wraps sio.EncryptReader and
// underlying hash.Reader in a struct
type PutObjReader struct {
*hash.Reader // actual data stream
rawReader *hash.Reader // original data stream
sealMD5Fn SealMD5CurrFn
}
// Size returns the absolute number of bytes the Reader
// will return during reading. It returns -1 for unlimited
// data.
func (p *PutObjReader) Size() int64 {
return p.Reader.Size()
}
// MD5CurrentHexString returns the current MD5Sum or encrypted MD5Sum
// as a hex encoded string
func (p *PutObjReader) MD5CurrentHexString() string {
md5sumCurr := p.rawReader.MD5Current()
if p.sealMD5Fn != nil {
md5sumCurr = p.sealMD5Fn(md5sumCurr)
}
return hex.EncodeToString(md5sumCurr)
}
// NewPutObjReader returns a new PutObjReader and holds
// reference to underlying data stream from client and the encrypted
// data reader
func NewPutObjReader(rawReader *hash.Reader, encReader *hash.Reader, encKey []byte) *PutObjReader {
p := PutObjReader{Reader: rawReader, rawReader: rawReader}
if len(encKey) != 0 && encReader != nil {
var objKey crypto.ObjectKey
copy(objKey[:], encKey)
p.sealMD5Fn = sealETagFn(objKey)
p.Reader = encReader
}
return &p
}
func sealETag(encKey crypto.ObjectKey, md5CurrSum []byte) []byte {
var emptyKey [32]byte
if bytes.Equal(encKey[:], emptyKey[:]) {
return md5CurrSum
}
return encKey.SealETag(md5CurrSum)
}
func sealETagFn(key crypto.ObjectKey) SealMD5CurrFn {
fn := func(md5sumcurr []byte) []byte {
return sealETag(key, md5sumcurr)
}
return fn
}
+6 -6
View File
@@ -75,7 +75,7 @@ func checkCopyObjectPreconditions(w http.ResponseWriter, r *http.Request, objInf
if !ifModifiedSince(objInfo.ModTime, givenTime) { if !ifModifiedSince(objInfo.ModTime, givenTime) {
// If the object is not modified since the specified time. // If the object is not modified since the specified time.
writeHeaders() writeHeaders()
writeErrorResponse(w, ErrPreconditionFailed, r.URL) writeErrorResponse(w, ErrPreconditionFailed, r.URL, guessIsBrowserReq(r))
return true return true
} }
} }
@@ -89,7 +89,7 @@ func checkCopyObjectPreconditions(w http.ResponseWriter, r *http.Request, objInf
if ifModifiedSince(objInfo.ModTime, givenTime) { if ifModifiedSince(objInfo.ModTime, givenTime) {
// If the object is modified since the specified time. // If the object is modified since the specified time.
writeHeaders() writeHeaders()
writeErrorResponse(w, ErrPreconditionFailed, r.URL) writeErrorResponse(w, ErrPreconditionFailed, r.URL, guessIsBrowserReq(r))
return true return true
} }
} }
@@ -102,7 +102,7 @@ func checkCopyObjectPreconditions(w http.ResponseWriter, r *http.Request, objInf
if objInfo.ETag != "" && !isETagEqual(objInfo.ETag, ifMatchETagHeader) { if objInfo.ETag != "" && !isETagEqual(objInfo.ETag, ifMatchETagHeader) {
// If the object ETag does not match with the specified ETag. // If the object ETag does not match with the specified ETag.
writeHeaders() writeHeaders()
writeErrorResponse(w, ErrPreconditionFailed, r.URL) writeErrorResponse(w, ErrPreconditionFailed, r.URL, guessIsBrowserReq(r))
return true return true
} }
} }
@@ -114,7 +114,7 @@ func checkCopyObjectPreconditions(w http.ResponseWriter, r *http.Request, objInf
if objInfo.ETag != "" && isETagEqual(objInfo.ETag, ifNoneMatchETagHeader) { if objInfo.ETag != "" && isETagEqual(objInfo.ETag, ifNoneMatchETagHeader) {
// If the object ETag matches with the specified ETag. // If the object ETag matches with the specified ETag.
writeHeaders() writeHeaders()
writeErrorResponse(w, ErrPreconditionFailed, r.URL) writeErrorResponse(w, ErrPreconditionFailed, r.URL, guessIsBrowserReq(r))
return true return true
} }
} }
@@ -174,7 +174,7 @@ func checkPreconditions(w http.ResponseWriter, r *http.Request, objInfo ObjectIn
if ifModifiedSince(objInfo.ModTime, givenTime) { if ifModifiedSince(objInfo.ModTime, givenTime) {
// If the object is modified since the specified time. // If the object is modified since the specified time.
writeHeaders() writeHeaders()
writeErrorResponse(w, ErrPreconditionFailed, r.URL) writeErrorResponse(w, ErrPreconditionFailed, r.URL, guessIsBrowserReq(r))
return true return true
} }
} }
@@ -187,7 +187,7 @@ func checkPreconditions(w http.ResponseWriter, r *http.Request, objInfo ObjectIn
if !isETagEqual(objInfo.ETag, ifMatchETagHeader) { if !isETagEqual(objInfo.ETag, ifMatchETagHeader) {
// If the object ETag does not match with the specified ETag. // If the object ETag does not match with the specified ETag.
writeHeaders() writeHeaders()
writeErrorResponse(w, ErrPreconditionFailed, r.URL) writeErrorResponse(w, ErrPreconditionFailed, r.URL, guessIsBrowserReq(r))
return true return true
} }
} }
+352 -218
View File
File diff suppressed because it is too large Load Diff
+34 -13
View File
@@ -21,6 +21,7 @@ import (
"context" "context"
"crypto/md5" "crypto/md5"
"encoding/base64" "encoding/base64"
"encoding/hex"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"io" "io"
@@ -83,7 +84,7 @@ func testAPIHeadObjectHandler(obj ObjectLayer, instanceType, bucketName string,
// iterate through the above set of inputs and upload the object. // iterate through the above set of inputs and upload the object.
for i, input := range putObjectInputs { for i, input := range putObjectInputs {
// uploading the object. // uploading the object.
_, err := obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetHashReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, ObjectOptions{}) _, err := obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, ObjectOptions{})
// if object upload fails stop the test. // if object upload fails stop the test.
if err != nil { if err != nil {
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err) t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
@@ -355,7 +356,7 @@ func testAPIGetObjectHandler(obj ObjectLayer, instanceType, bucketName string, a
// iterate through the above set of inputs and upload the object. // iterate through the above set of inputs and upload the object.
for i, input := range putObjectInputs { for i, input := range putObjectInputs {
// uploading the object. // uploading the object.
_, err := obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetHashReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, ObjectOptions{}) _, err := obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, ObjectOptions{})
// if object upload fails stop the test. // if object upload fails stop the test.
if err != nil { if err != nil {
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err) t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
@@ -1391,7 +1392,7 @@ func testAPICopyObjectPartHandlerSanity(obj ObjectLayer, instanceType, bucketNam
for i, input := range putObjectInputs { for i, input := range putObjectInputs {
// uploading the object. // uploading the object.
_, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, _, err = obj.PutObject(context.Background(), input.bucketName, input.objectName,
mustGetHashReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, opts) mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, opts)
// if object upload fails stop the test. // if object upload fails stop the test.
if err != nil { if err != nil {
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err) t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
@@ -1449,7 +1450,7 @@ func testAPICopyObjectPartHandlerSanity(obj ObjectLayer, instanceType, bucketNam
}) })
} }
result, err := obj.CompleteMultipartUpload(context.Background(), bucketName, testObject, uploadID, parts) result, err := obj.CompleteMultipartUpload(context.Background(), bucketName, testObject, uploadID, parts, ObjectOptions{})
if err != nil { if err != nil {
t.Fatalf("Test: %s complete multipart upload failed: <ERROR> %v", instanceType, err) t.Fatalf("Test: %s complete multipart upload failed: <ERROR> %v", instanceType, err)
} }
@@ -1501,7 +1502,7 @@ func testAPICopyObjectPartHandler(obj ObjectLayer, instanceType, bucketName stri
// iterate through the above set of inputs and upload the object. // iterate through the above set of inputs and upload the object.
for i, input := range putObjectInputs { for i, input := range putObjectInputs {
// uploading the object. // uploading the object.
_, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetHashReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, opts) _, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, opts)
// if object upload fails stop the test. // if object upload fails stop the test.
if err != nil { if err != nil {
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err) t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
@@ -1811,9 +1812,13 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
// this is required even to assert the copied object, // this is required even to assert the copied object,
bytesData := []struct { bytesData := []struct {
byteData []byte byteData []byte
md5sum string
}{ }{
{generateBytesData(6 * humanize.KiByte)}, {byteData: generateBytesData(6 * humanize.KiByte)},
} }
h := md5.New()
h.Write(bytesData[0].byteData)
bytesData[0].md5sum = hex.EncodeToString(h.Sum(nil))
buffers := []*bytes.Buffer{ buffers := []*bytes.Buffer{
new(bytes.Buffer), new(bytes.Buffer),
@@ -1826,23 +1831,29 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
objectName string objectName string
contentLength int64 contentLength int64
textData []byte textData []byte
md5sum string
metaData map[string]string metaData map[string]string
}{ }{
// case - 1. // case - 1.
{bucketName, objectName, int64(len(bytesData[0].byteData)), bytesData[0].byteData, make(map[string]string)}, {bucketName, objectName, int64(len(bytesData[0].byteData)), bytesData[0].byteData, bytesData[0].md5sum, make(map[string]string)},
// case - 2. // case - 2.
// used for anonymous HTTP request test. // used for anonymous HTTP request test.
{bucketName, anonObject, int64(len(bytesData[0].byteData)), bytesData[0].byteData, make(map[string]string)}, {bucketName, anonObject, int64(len(bytesData[0].byteData)), bytesData[0].byteData, bytesData[0].md5sum, make(map[string]string)},
} }
// iterate through the above set of inputs and upload the object. // iterate through the above set of inputs and upload the object.
for i, input := range putObjectInputs { for i, input := range putObjectInputs {
// uploading the object. // uploading the object.
_, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetHashReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, opts) var objInfo ObjectInfo
objInfo, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, opts)
// if object upload fails stop the test. // if object upload fails stop the test.
if err != nil { if err != nil {
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err) t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
} }
if objInfo.ETag != input.md5sum {
t.Fatalf("Put Object case %d: Checksum mismatched: <ERROR> got %s, expected %s", i+1, input.md5sum, objInfo.ETag)
}
} }
// test cases with inputs and expected result for Copy Object. // test cases with inputs and expected result for Copy Object.
@@ -2105,6 +2116,16 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
t.Fatalf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, rec.Code) t.Fatalf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, rec.Code)
} }
if rec.Code == http.StatusOK { if rec.Code == http.StatusOK {
var cpObjResp CopyObjectResponse
if err = xml.Unmarshal(rec.Body.Bytes(), &cpObjResp); err != nil {
t.Fatalf("Test %d: %s: Failed to parse the CopyObjectResult response: <ERROR> %s", i+1, instanceType, err)
}
retag := canonicalizeETag(cpObjResp.ETag)
if retag != bytesData[0].md5sum {
t.Fatalf("Test %d: %s: Failed to CopyObject: <ERROR> got %s, expected %s", i+1, instanceType, retag, bytesData[0].md5sum)
}
// See if the new object is formed. // See if the new object is formed.
// testing whether the copy was successful. // testing whether the copy was successful.
err = obj.GetObject(context.Background(), testCase.bucketName, testCase.newObjectName, 0, int64(len(bytesData[0].byteData)), buffers[0], "", opts) err = obj.GetObject(context.Background(), testCase.bucketName, testCase.newObjectName, 0, int64(len(bytesData[0].byteData)), buffers[0], "", opts)
@@ -2462,7 +2483,7 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s
// Iterating over creatPartCases to generate multipart chunks. // Iterating over creatPartCases to generate multipart chunks.
for _, part := range parts { for _, part := range parts {
_, err = obj.PutObjectPart(context.Background(), part.bucketName, part.objName, part.uploadID, part.PartID, _, err = obj.PutObjectPart(context.Background(), part.bucketName, part.objName, part.uploadID, part.PartID,
mustGetHashReader(t, bytes.NewBufferString(part.inputReaderData), part.intputDataSize, part.inputMd5, ""), opts) mustGetPutObjReader(t, bytes.NewBufferString(part.inputReaderData), part.intputDataSize, part.inputMd5, ""), opts)
if err != nil { if err != nil {
t.Fatalf("%s : %s", instanceType, err) t.Fatalf("%s : %s", instanceType, err)
} }
@@ -2813,7 +2834,7 @@ func testAPIAbortMultipartHandler(obj ObjectLayer, instanceType, bucketName stri
// Iterating over createPartCases to generate multipart chunks. // Iterating over createPartCases to generate multipart chunks.
for _, part := range parts { for _, part := range parts {
_, err = obj.PutObjectPart(context.Background(), part.bucketName, part.objName, part.uploadID, part.PartID, _, err = obj.PutObjectPart(context.Background(), part.bucketName, part.objName, part.uploadID, part.PartID,
mustGetHashReader(t, bytes.NewBufferString(part.inputReaderData), part.intputDataSize, part.inputMd5, ""), opts) mustGetPutObjReader(t, bytes.NewBufferString(part.inputReaderData), part.intputDataSize, part.inputMd5, ""), opts)
if err != nil { if err != nil {
t.Fatalf("%s : %s", instanceType, err) t.Fatalf("%s : %s", instanceType, err)
} }
@@ -2951,7 +2972,7 @@ func testAPIDeleteObjectHandler(obj ObjectLayer, instanceType, bucketName string
// iterate through the above set of inputs and upload the object. // iterate through the above set of inputs and upload the object.
for i, input := range putObjectInputs { for i, input := range putObjectInputs {
// uploading the object. // uploading the object.
_, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetHashReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, opts) _, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData[""], ""), input.metaData, opts)
// if object upload fails stop the test. // if object upload fails stop the test.
if err != nil { if err != nil {
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err) t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
@@ -3657,7 +3678,7 @@ func testAPIListObjectPartsHandler(obj ObjectLayer, instanceType, bucketName str
uploadIDCopy := uploadID uploadIDCopy := uploadID
// create an object Part, will be used to test list object parts. // create an object Part, will be used to test list object parts.
_, err = obj.PutObjectPart(context.Background(), bucketName, testObject, uploadID, 1, mustGetHashReader(t, bytes.NewReader([]byte("hello")), int64(len("hello")), "5d41402abc4b2a76b9719d911017c592", ""), opts) _, err = obj.PutObjectPart(context.Background(), bucketName, testObject, uploadID, 1, mustGetPutObjReader(t, bytes.NewReader([]byte("hello")), int64(len("hello")), "5d41402abc4b2a76b9719d911017c592", ""), opts)
if err != nil { if err != nil {
t.Fatalf("Minio %s : %s.", instanceType, err) t.Fatalf("Minio %s : %s.", instanceType, err)
} }
+18 -18
View File
@@ -106,7 +106,7 @@ func testMultipartObjectCreation(obj ObjectLayer, instanceType string, t TestErr
expectedETaghex := getMD5Hash(data) expectedETaghex := getMD5Hash(data)
var calcPartInfo PartInfo var calcPartInfo PartInfo
calcPartInfo, err = obj.PutObjectPart(context.Background(), "bucket", "key", uploadID, i, mustGetHashReader(t, bytes.NewBuffer(data), int64(len(data)), expectedETaghex, ""), opts) calcPartInfo, err = obj.PutObjectPart(context.Background(), "bucket", "key", uploadID, i, mustGetPutObjReader(t, bytes.NewBuffer(data), int64(len(data)), expectedETaghex, ""), opts)
if err != nil { if err != nil {
t.Errorf("%s: <ERROR> %s", instanceType, err) t.Errorf("%s: <ERROR> %s", instanceType, err)
} }
@@ -118,7 +118,7 @@ func testMultipartObjectCreation(obj ObjectLayer, instanceType string, t TestErr
ETag: calcPartInfo.ETag, ETag: calcPartInfo.ETag,
}) })
} }
objInfo, err := obj.CompleteMultipartUpload(context.Background(), "bucket", "key", uploadID, completedParts.Parts) objInfo, err := obj.CompleteMultipartUpload(context.Background(), "bucket", "key", uploadID, completedParts.Parts, ObjectOptions{})
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
@@ -157,7 +157,7 @@ func testMultipartObjectAbort(obj ObjectLayer, instanceType string, t TestErrHan
metadata["md5"] = expectedETaghex metadata["md5"] = expectedETaghex
var calcPartInfo PartInfo var calcPartInfo PartInfo
calcPartInfo, err = obj.PutObjectPart(context.Background(), "bucket", "key", uploadID, i, mustGetHashReader(t, bytes.NewBufferString(randomString), int64(len(randomString)), expectedETaghex, ""), opts) calcPartInfo, err = obj.PutObjectPart(context.Background(), "bucket", "key", uploadID, i, mustGetPutObjReader(t, bytes.NewBufferString(randomString), int64(len(randomString)), expectedETaghex, ""), opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
@@ -199,7 +199,7 @@ func testMultipleObjectCreation(obj ObjectLayer, instanceType string, t TestErrH
metadata := make(map[string]string) metadata := make(map[string]string)
metadata["etag"] = expectedETaghex metadata["etag"] = expectedETaghex
var objInfo ObjectInfo var objInfo ObjectInfo
objInfo, err = obj.PutObject(context.Background(), "bucket", key, mustGetHashReader(t, bytes.NewBufferString(randomString), int64(len(randomString)), metadata["etag"], ""), metadata, opts) objInfo, err = obj.PutObject(context.Background(), "bucket", key, mustGetPutObjReader(t, bytes.NewBufferString(randomString), int64(len(randomString)), metadata["etag"], ""), metadata, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
@@ -253,7 +253,7 @@ func testPaging(obj ObjectLayer, instanceType string, t TestErrHandler) {
// check before paging occurs. // check before paging occurs.
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
key := "obj" + strconv.Itoa(i) key := "obj" + strconv.Itoa(i)
_, err = obj.PutObject(context.Background(), "bucket", key, mustGetHashReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "bucket", key, mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
@@ -273,7 +273,7 @@ func testPaging(obj ObjectLayer, instanceType string, t TestErrHandler) {
// check after paging occurs pages work. // check after paging occurs pages work.
for i := 6; i <= 10; i++ { for i := 6; i <= 10; i++ {
key := "obj" + strconv.Itoa(i) key := "obj" + strconv.Itoa(i)
_, err = obj.PutObject(context.Background(), "bucket", key, mustGetHashReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "bucket", key, mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
@@ -290,11 +290,11 @@ func testPaging(obj ObjectLayer, instanceType string, t TestErrHandler) {
} }
// check paging with prefix at end returns less objects. // check paging with prefix at end returns less objects.
{ {
_, err = obj.PutObject(context.Background(), "bucket", "newPrefix", mustGetHashReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "bucket", "newPrefix", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
_, err = obj.PutObject(context.Background(), "bucket", "newPrefix2", mustGetHashReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "bucket", "newPrefix2", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
@@ -332,11 +332,11 @@ func testPaging(obj ObjectLayer, instanceType string, t TestErrHandler) {
// check delimited results with delimiter and prefix. // check delimited results with delimiter and prefix.
{ {
_, err = obj.PutObject(context.Background(), "bucket", "this/is/delimited", mustGetHashReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "bucket", "this/is/delimited", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
_, err = obj.PutObject(context.Background(), "bucket", "this/is/also/a/delimited/file", mustGetHashReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "bucket", "this/is/also/a/delimited/file", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
@@ -448,14 +448,14 @@ func testObjectOverwriteWorks(obj ObjectLayer, instanceType string, t TestErrHan
var opts ObjectOptions var opts ObjectOptions
uploadContent := "The list of parts was not in ascending order. The parts list must be specified in order by part number." uploadContent := "The list of parts was not in ascending order. The parts list must be specified in order by part number."
length := int64(len(uploadContent)) length := int64(len(uploadContent))
_, err = obj.PutObject(context.Background(), "bucket", "object", mustGetHashReader(t, bytes.NewBufferString(uploadContent), length, "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "bucket", "object", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), length, "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
uploadContent = "The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed." uploadContent = "The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed."
length = int64(len(uploadContent)) length = int64(len(uploadContent))
_, err = obj.PutObject(context.Background(), "bucket", "object", mustGetHashReader(t, bytes.NewBufferString(uploadContent), length, "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "bucket", "object", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), length, "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
@@ -478,7 +478,7 @@ func (s *ObjectLayerAPISuite) TestNonExistantBucketOperations(t *testing.T) {
// Tests validate that bucket operation on non-existent bucket fails. // Tests validate that bucket operation on non-existent bucket fails.
func testNonExistantBucketOperations(obj ObjectLayer, instanceType string, t TestErrHandler) { func testNonExistantBucketOperations(obj ObjectLayer, instanceType string, t TestErrHandler) {
var opts ObjectOptions var opts ObjectOptions
_, err := obj.PutObject(context.Background(), "bucket1", "object", mustGetHashReader(t, bytes.NewBufferString("one"), int64(len("one")), "", ""), nil, opts) _, err := obj.PutObject(context.Background(), "bucket1", "object", mustGetPutObjReader(t, bytes.NewBufferString("one"), int64(len("one")), "", ""), nil, opts)
if err == nil { if err == nil {
t.Fatal("Expected error but found nil") t.Fatal("Expected error but found nil")
} }
@@ -526,7 +526,7 @@ func testPutObject(obj ObjectLayer, instanceType string, t TestErrHandler) {
var bytesBuffer1 bytes.Buffer var bytesBuffer1 bytes.Buffer
var opts ObjectOptions var opts ObjectOptions
_, err = obj.PutObject(context.Background(), "bucket", "object", mustGetHashReader(t, readerEOF, length, "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "bucket", "object", mustGetPutObjReader(t, readerEOF, length, "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
@@ -539,7 +539,7 @@ func testPutObject(obj ObjectLayer, instanceType string, t TestErrHandler) {
} }
var bytesBuffer2 bytes.Buffer var bytesBuffer2 bytes.Buffer
_, err = obj.PutObject(context.Background(), "bucket", "object", mustGetHashReader(t, readerNoEOF, length, "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "bucket", "object", mustGetPutObjReader(t, readerNoEOF, length, "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
@@ -568,7 +568,7 @@ func testPutObjectInSubdir(obj ObjectLayer, instanceType string, t TestErrHandle
uploadContent := `The specified multipart upload does not exist. The upload ID might be invalid, or the multipart uploadContent := `The specified multipart upload does not exist. The upload ID might be invalid, or the multipart
upload might have been aborted or completed.` upload might have been aborted or completed.`
length := int64(len(uploadContent)) length := int64(len(uploadContent))
_, err = obj.PutObject(context.Background(), "bucket", "dir1/dir2/object", mustGetHashReader(t, bytes.NewBufferString(uploadContent), length, "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "bucket", "dir1/dir2/object", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), length, "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
@@ -741,7 +741,7 @@ func testGetDirectoryReturnsObjectNotFound(obj ObjectLayer, instanceType string,
content := "One or more of the specified parts could not be found. The part might not have been uploaded, or the specified entity tag might not have matched the part's entity tag." content := "One or more of the specified parts could not be found. The part might not have been uploaded, or the specified entity tag might not have matched the part's entity tag."
length := int64(len(content)) length := int64(len(content))
var opts ObjectOptions var opts ObjectOptions
_, err = obj.PutObject(context.Background(), bucketName, "dir1/dir3/object", mustGetHashReader(t, bytes.NewBufferString(content), length, "", ""), nil, opts) _, err = obj.PutObject(context.Background(), bucketName, "dir1/dir3/object", mustGetPutObjReader(t, bytes.NewBufferString(content), length, "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
@@ -783,7 +783,7 @@ func testContentType(obj ObjectLayer, instanceType string, t TestErrHandler) {
var opts ObjectOptions var opts ObjectOptions
uploadContent := "The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed." uploadContent := "The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed."
// Test empty. // Test empty.
_, err = obj.PutObject(context.Background(), "bucket", "minio.png", mustGetHashReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts) _, err = obj.PutObject(context.Background(), "bucket", "minio.png", mustGetPutObjReader(t, bytes.NewBufferString(uploadContent), int64(len(uploadContent)), "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err) t.Fatalf("%s: <ERROR> %s", instanceType, err)
} }
@@ -1,4 +1,4 @@
// +build plan9 // +build plan9 solaris
/* /*
* Minio Cloud Storage, (C) 2016, 2017, 2018 Minio, Inc. * Minio Cloud Storage, (C) 2016, 2017, 2018 Minio, Inc.
+1 -1
View File
@@ -1,4 +1,4 @@
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris // +build darwin dragonfly freebsd linux nacl netbsd openbsd
/* /*
* Minio Cloud Storage, (C) 2016, 2017, 2018 Minio, Inc. * Minio Cloud Storage, (C) 2016, 2017, 2018 Minio, Inc.
+6 -2
View File
@@ -83,8 +83,10 @@ func checkPathLength(pathName string) error {
return errFileNameTooLong return errFileNameTooLong
} }
// Convert any '\' to '/'. if runtime.GOOS == "windows" {
pathName = filepath.ToSlash(pathName) // Convert any '\' to '/'.
pathName = filepath.ToSlash(pathName)
}
// Check each path segment length is > 255 // Check each path segment length is > 255
for len(pathName) > 0 && pathName != "." && pathName != "/" { for len(pathName) > 0 && pathName != "." && pathName != "/" {
@@ -374,6 +376,7 @@ func (s *posix) diskUsage(doneCh chan struct{}) {
default: default:
fi, err := os.Stat(entry) fi, err := os.Stat(entry)
if err != nil { if err != nil {
err = osErrToFSFileErr(err)
return err return err
} }
atomic.AddUint64(&s.totalUsed, uint64(fi.Size())) atomic.AddUint64(&s.totalUsed, uint64(fi.Size()))
@@ -413,6 +416,7 @@ func (s *posix) diskUsage(doneCh chan struct{}) {
default: default:
fi, err := os.Stat(entry) fi, err := os.Stat(entry)
if err != nil { if err != nil {
err = osErrToFSFileErr(err)
return err return err
} }
usage = usage + uint64(fi.Size()) usage = usage + uint64(fi.Size())
+10 -10
View File
@@ -128,6 +128,15 @@ func connectLoadInitFormats(retryCount int, firstDisk bool, endpoints EndpointLi
} }
defer closeStorageDisks(storageDisks) defer closeStorageDisks(storageDisks)
// Attempt to load all `format.json` from all disks.
formatConfigs, sErrs := loadFormatXLAll(storageDisks)
// Check if we have
for i, sErr := range sErrs {
if _, ok := formatCriticalErrors[sErr]; ok {
return nil, fmt.Errorf("Disk %s: %s", endpoints[i], sErr)
}
}
// Connect to all storage disks, a connection failure will be // Connect to all storage disks, a connection failure will be
// only logged after some retries. // only logged after some retries.
for _, disk := range storageDisks { for _, disk := range storageDisks {
@@ -139,15 +148,6 @@ func connectLoadInitFormats(retryCount int, firstDisk bool, endpoints EndpointLi
} }
} }
// Attempt to load all `format.json` from all disks.
formatConfigs, sErrs := loadFormatXLAll(storageDisks)
// Check if we have
for i, sErr := range sErrs {
if _, ok := formatCriticalErrors[sErr]; ok {
return nil, fmt.Errorf("Disk %s: %s", endpoints[i], sErr)
}
}
// Pre-emptively check if one of the formatted disks // Pre-emptively check if one of the formatted disks
// is invalid. This function returns success for the // is invalid. This function returns success for the
// most part unless one of the formats is not consistent // most part unless one of the formats is not consistent
@@ -210,7 +210,7 @@ func connectLoadInitFormats(retryCount int, firstDisk bool, endpoints EndpointLi
} }
} }
logger.SetDeploymentID(format.ID) globalDeploymentID = format.ID
if err = formatXLFixLocalDeploymentID(context.Background(), storageDisks, format); err != nil { if err = formatXLFixLocalDeploymentID(context.Background(), storageDisks, format); err != nil {
return nil, err return nil, err
+30 -21
View File
@@ -35,9 +35,10 @@ const DefaultRESTTimeout = 1 * time.Minute
// Client - http based RPC client. // Client - http based RPC client.
type Client struct { type Client struct {
httpClient *http.Client httpClient *http.Client
url *url.URL httpIdleConnsCloser func()
newAuthToken func() string url *url.URL
newAuthToken func() string
} }
// Call - make a REST call. // Call - make a REST call.
@@ -67,6 +68,13 @@ func (c *Client) Call(method string, values url.Values, body io.Reader) (reply i
return resp.Body, nil return resp.Body, nil
} }
// Close closes all idle connections of the underlying http client
func (c *Client) Close() {
if c.httpIdleConnsCloser != nil {
c.httpIdleConnsCloser()
}
}
func newCustomDialContext(timeout time.Duration) func(ctx context.Context, network, addr string) (net.Conn, error) { func newCustomDialContext(timeout time.Duration) func(ctx context.Context, network, addr string) (net.Conn, error) {
return func(ctx context.Context, network, addr string) (net.Conn, error) { return func(ctx context.Context, network, addr string) (net.Conn, error) {
dialer := &net.Dialer{ dialer := &net.Dialer{
@@ -84,25 +92,26 @@ func newCustomDialContext(timeout time.Duration) func(ctx context.Context, netwo
} }
} }
// NewClient - returns new RPC client. // NewClient - returns new REST client.
func NewClient(url *url.URL, tlsConfig *tls.Config, timeout time.Duration, newAuthToken func() string) *Client { func NewClient(url *url.URL, tlsConfig *tls.Config, timeout time.Duration, newAuthToken func() string) *Client {
// Transport is exactly same as Go default in https://golang.org/pkg/net/http/#RoundTripper
// except custom DialContext and TLSClientConfig.
tr := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: newCustomDialContext(timeout),
MaxIdleConnsPerHost: 4096,
MaxIdleConns: 4096,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: tlsConfig,
DisableCompression: true,
}
return &Client{ return &Client{
httpClient: &http.Client{ httpClient: &http.Client{Transport: tr},
// Transport is exactly same as Go default in https://golang.org/pkg/net/http/#RoundTripper httpIdleConnsCloser: tr.CloseIdleConnections,
// except custom DialContext and TLSClientConfig. url: url,
Transport: &http.Transport{ newAuthToken: newAuthToken,
Proxy: http.ProxyFromEnvironment,
DialContext: newCustomDialContext(timeout),
MaxIdleConnsPerHost: 4096,
MaxIdleConns: 4096,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: tlsConfig,
DisableCompression: true,
},
},
url: url,
newAuthToken: newAuthToken,
} }
} }
+2 -2
View File
@@ -47,8 +47,8 @@ func registerDistXLRouters(router *mux.Router, endpoints EndpointList) {
// List of some generic handlers which are applied for all incoming requests. // List of some generic handlers which are applied for all incoming requests.
var globalHandlers = []HandlerFunc{ var globalHandlers = []HandlerFunc{
// set x-amz-request-id header. // set x-amz-request-id, x-minio-deployment-id header.
addrequestIDHeader, addCustomHeaders,
// set HTTP security headers such as Content-Security-Policy. // set HTTP security headers such as Content-Security-Policy.
addSecurityHeaders, addSecurityHeaders,
// Forward path style requests to actual host in a bucket federated setup. // Forward path style requests to actual host in a bucket federated setup.
+5 -5
View File
@@ -41,13 +41,13 @@ var serverFlags = []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "address", Name: "address",
Value: ":" + globalMinioPort, Value: ":" + globalMinioPort,
Usage: "Bind to a specific ADDRESS:PORT, ADDRESS can be an IP or hostname.", Usage: "bind to a specific ADDRESS:PORT, ADDRESS can be an IP or hostname",
}, },
} }
var serverCmd = cli.Command{ var serverCmd = cli.Command{
Name: "server", Name: "server",
Usage: "Start object storage server.", Usage: "start object storage server",
Flags: append(serverFlags, globalFlags...), Flags: append(serverFlags, globalFlags...),
Action: serverMain, Action: serverMain,
CustomHelpTemplate: `NAME: CustomHelpTemplate: `NAME:
@@ -112,10 +112,10 @@ EXAMPLES:
4. Start erasure coded minio server on a node with 64 drives. 4. Start erasure coded minio server on a node with 64 drives.
$ {{.HelpName}} /mnt/export{1...64} $ {{.HelpName}} /mnt/export{1...64}
5. Start distributed minio server on an 8 node setup with 8 drives each. Run following command on all the 8 nodes. 5. Start distributed minio server on an 32 node setup with 32 drives each. Run following command on all the 32 nodes.
$ export MINIO_ACCESS_KEY=minio $ export MINIO_ACCESS_KEY=minio
$ export MINIO_SECRET_KEY=miniostorage $ export MINIO_SECRET_KEY=miniostorage
$ {{.HelpName}} http://node{1...8}.example.com/mnt/export/{1...8} $ {{.HelpName}} http://node{1...32}.example.com/mnt/export/{1...32}
6. Start minio server with edge caching enabled. 6. Start minio server with edge caching enabled.
$ export MINIO_CACHE_DRIVES="/mnt/drive1;/mnt/drive2;/mnt/drive3;/mnt/drive4" $ export MINIO_CACHE_DRIVES="/mnt/drive1;/mnt/drive2;/mnt/drive3;/mnt/drive4"
@@ -381,7 +381,7 @@ func serverMain(ctx *cli.Context) {
// Initialize notification system. // Initialize notification system.
if err = globalNotificationSys.Init(newObject); err != nil { if err = globalNotificationSys.Init(newObject); err != nil {
logger.Fatal(err, "Unable to initialize notification system") logger.LogIf(context.Background(), err)
} }
globalObjLayerMutex.Lock() globalObjLayerMutex.Lock()
+5
View File
@@ -28,6 +28,9 @@ const (
// DistXLSetupType - Distributed XL setup type enum. // DistXLSetupType - Distributed XL setup type enum.
DistXLSetupType DistXLSetupType
// GatewaySetupType - gateway setup type enum.
GatewaySetupType
) )
func (setupType SetupType) String() string { func (setupType SetupType) String() string {
@@ -38,6 +41,8 @@ func (setupType SetupType) String() string {
return globalMinioModeXL return globalMinioModeXL
case DistXLSetupType: case DistXLSetupType:
return globalMinioModeDistXL return globalMinioModeDistXL
case GatewaySetupType:
return globalMinioModeGatewayPrefix
} }
return "" return ""
+7 -7
View File
@@ -189,7 +189,7 @@ func testObjectQuorumFromMeta(obj ObjectLayer, instanceType string, dirs []strin
// Object for test case 1 - No StorageClass defined, no MetaData in PutObject // Object for test case 1 - No StorageClass defined, no MetaData in PutObject
object1 := "object1" object1 := "object1"
_, err = obj.PutObject(context.Background(), bucket, object1, mustGetHashReader(t, bytes.NewReader(data), int64(len(data)), "", ""), nil, opts) _, err = obj.PutObject(context.Background(), bucket, object1, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), nil, opts)
if err != nil { if err != nil {
t.Fatalf("Failed to putObject %v", err) t.Fatalf("Failed to putObject %v", err)
} }
@@ -200,7 +200,7 @@ func testObjectQuorumFromMeta(obj ObjectLayer, instanceType string, dirs []strin
object2 := "object2" object2 := "object2"
metadata2 := make(map[string]string) metadata2 := make(map[string]string)
metadata2["x-amz-storage-class"] = reducedRedundancyStorageClass metadata2["x-amz-storage-class"] = reducedRedundancyStorageClass
_, err = obj.PutObject(context.Background(), bucket, object2, mustGetHashReader(t, bytes.NewReader(data), int64(len(data)), "", ""), metadata2, opts) _, err = obj.PutObject(context.Background(), bucket, object2, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), metadata2, opts)
if err != nil { if err != nil {
t.Fatalf("Failed to putObject %v", err) t.Fatalf("Failed to putObject %v", err)
} }
@@ -211,7 +211,7 @@ func testObjectQuorumFromMeta(obj ObjectLayer, instanceType string, dirs []strin
object3 := "object3" object3 := "object3"
metadata3 := make(map[string]string) metadata3 := make(map[string]string)
metadata3["x-amz-storage-class"] = standardStorageClass metadata3["x-amz-storage-class"] = standardStorageClass
_, err = obj.PutObject(context.Background(), bucket, object3, mustGetHashReader(t, bytes.NewReader(data), int64(len(data)), "", ""), metadata3, opts) _, err = obj.PutObject(context.Background(), bucket, object3, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), metadata3, opts)
if err != nil { if err != nil {
t.Fatalf("Failed to putObject %v", err) t.Fatalf("Failed to putObject %v", err)
} }
@@ -227,7 +227,7 @@ func testObjectQuorumFromMeta(obj ObjectLayer, instanceType string, dirs []strin
Scheme: "EC", Scheme: "EC",
} }
_, err = obj.PutObject(context.Background(), bucket, object4, mustGetHashReader(t, bytes.NewReader(data), int64(len(data)), "", ""), metadata4, opts) _, err = obj.PutObject(context.Background(), bucket, object4, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), metadata4, opts)
if err != nil { if err != nil {
t.Fatalf("Failed to putObject %v", err) t.Fatalf("Failed to putObject %v", err)
} }
@@ -245,7 +245,7 @@ func testObjectQuorumFromMeta(obj ObjectLayer, instanceType string, dirs []strin
Scheme: "EC", Scheme: "EC",
} }
_, err = obj.PutObject(context.Background(), bucket, object5, mustGetHashReader(t, bytes.NewReader(data), int64(len(data)), "", ""), metadata5, opts) _, err = obj.PutObject(context.Background(), bucket, object5, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), metadata5, opts)
if err != nil { if err != nil {
t.Fatalf("Failed to putObject %v", err) t.Fatalf("Failed to putObject %v", err)
} }
@@ -263,7 +263,7 @@ func testObjectQuorumFromMeta(obj ObjectLayer, instanceType string, dirs []strin
Scheme: "EC", Scheme: "EC",
} }
_, err = obj.PutObject(context.Background(), bucket, object6, mustGetHashReader(t, bytes.NewReader(data), int64(len(data)), "", ""), metadata6, opts) _, err = obj.PutObject(context.Background(), bucket, object6, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), metadata6, opts)
if err != nil { if err != nil {
t.Fatalf("Failed to putObject %v", err) t.Fatalf("Failed to putObject %v", err)
} }
@@ -281,7 +281,7 @@ func testObjectQuorumFromMeta(obj ObjectLayer, instanceType string, dirs []strin
Scheme: "EC", Scheme: "EC",
} }
_, err = obj.PutObject(context.Background(), bucket, object7, mustGetHashReader(t, bytes.NewReader(data), int64(len(data)), "", ""), metadata7, opts) _, err = obj.PutObject(context.Background(), bucket, object7, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), metadata7, opts)
if err != nil { if err != nil {
t.Fatalf("Failed to putObject %v", err) t.Fatalf("Failed to putObject %v", err)
} }
+1
View File
@@ -322,6 +322,7 @@ func (client *storageRESTClient) RenameFile(srcVolume, srcPath, dstVolume, dstPa
// Close - marks the client as closed. // Close - marks the client as closed.
func (client *storageRESTClient) Close() error { func (client *storageRESTClient) Close() error {
client.connected = false client.connected = false
client.restClient.Close()
return nil return nil
} }
+1 -1
View File
@@ -16,7 +16,7 @@
package cmd package cmd
const storageRESTVersion = "v1" const storageRESTVersion = "v2"
const storageRESTPath = minioReservedBucketPath + "/storage/" + storageRESTVersion + "/" const storageRESTPath = minioReservedBucketPath + "/storage/" + storageRESTVersion + "/"
const ( const (
+2 -1
View File
@@ -377,6 +377,7 @@ func registerStorageRESTHandlers(router *mux.Router, endpoints EndpointList) {
Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) Queries(restQueries(storageRESTVolume, storageRESTFilePath)...)
subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodRenameFile).HandlerFunc(httpTraceHdrs(server.RenameFileHandler)). subrouter.Methods(http.MethodPost).Path("/" + storageRESTMethodRenameFile).HandlerFunc(httpTraceHdrs(server.RenameFileHandler)).
Queries(restQueries(storageRESTSrcVolume, storageRESTSrcPath, storageRESTDstVolume, storageRESTDstPath)...) Queries(restQueries(storageRESTSrcVolume, storageRESTSrcPath, storageRESTDstVolume, storageRESTDstPath)...)
} }
router.NotFoundHandler = http.HandlerFunc(httpTraceAll(notFoundHandler))
} }
+2
View File
@@ -127,6 +127,8 @@ type ClientGrantsResult struct {
func (sts *stsAPIHandlers) AssumeRoleWithClientGrants(w http.ResponseWriter, r *http.Request) { func (sts *stsAPIHandlers) AssumeRoleWithClientGrants(w http.ResponseWriter, r *http.Request) {
ctx := newContext(r, w, "AssumeRoleWithClientGrants") ctx := newContext(r, w, "AssumeRoleWithClientGrants")
defer logger.AuditLog(w, r, "AssumeRoleWithClientGrants", nil)
if globalIAMValidators == nil { if globalIAMValidators == nil {
writeSTSErrorResponse(w, ErrSTSNotInitialized) writeSTSErrorResponse(w, ErrSTSNotInitialized)
return return
+2 -2
View File
@@ -136,12 +136,12 @@ func calculateSignedChunkLength(chunkDataSize int64) int64 {
2 // CRLF 2 // CRLF
} }
func mustGetHashReader(t TestErrHandler, data io.Reader, size int64, md5hex, sha256hex string) *hash.Reader { func mustGetPutObjReader(t TestErrHandler, data io.Reader, size int64, md5hex, sha256hex string) *PutObjReader {
hr, err := hash.NewReader(data, size, md5hex, sha256hex, size) hr, err := hash.NewReader(data, size, md5hex, sha256hex, size)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
return hr return NewPutObjReader(hr, nil, nil)
} }
// calculateSignedChunkLength - calculates the length of the overall stream (data + metadata) // calculateSignedChunkLength - calculates the length of the overall stream (data + metadata)
+2 -2
View File
@@ -40,12 +40,12 @@ import (
// Check for new software updates. // Check for new software updates.
var updateCmd = cli.Command{ var updateCmd = cli.Command{
Name: "update", Name: "update",
Usage: "Check for a new software update.", Usage: "update minio to latest release",
Action: mainUpdate, Action: mainUpdate,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "quiet", Name: "quiet",
Usage: "Disable any update prompt message.", Usage: "disable any update prompt message",
}, },
}, },
CustomHelpTemplate: `Name: CustomHelpTemplate: `Name:
+7 -6
View File
@@ -397,12 +397,13 @@ func newContext(r *http.Request, w http.ResponseWriter, api string) context.Cont
object = prefix object = prefix
} }
reqInfo := &logger.ReqInfo{ reqInfo := &logger.ReqInfo{
RequestID: w.Header().Get(responseRequestIDKey), DeploymentID: w.Header().Get(responseDeploymentIDKey),
RemoteHost: handlers.GetSourceIP(r), RequestID: w.Header().Get(responseRequestIDKey),
UserAgent: r.UserAgent(), RemoteHost: handlers.GetSourceIP(r),
API: api, UserAgent: r.UserAgent(),
BucketName: bucket, API: api,
ObjectName: object, BucketName: bucket,
ObjectName: object,
} }
return logger.SetReqInfo(context.Background(), reqInfo) return logger.SetReqInfo(context.Background(), reqInfo)
} }

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