Skip to content

Commit

Permalink
Pass on web-handler arguments properly to log entries (minio#7894)
Browse files Browse the repository at this point in the history
  • Loading branch information
krisis authored and kannappanr committed Jul 11, 2019
1 parent 5c0acbc commit ffd7b70
Show file tree
Hide file tree
Showing 3 changed files with 370 additions and 37 deletions.
244 changes: 244 additions & 0 deletions cmd/web-handler-context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/*
* MinIO Cloud Storage, (C) 2019 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 cmd

import (
"context"
"encoding/json"
"fmt"
"net/http"

"github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/handlers"
)

const (
kmBucket = "BucketName"
kmObject = "ObjectName"
kmObjects = "Objects"
kmPrefix = "Prefix"
kmMarker = "Marker"
kmUsername = "UserName"
kmHostname = "HostName"
kmPolicy = "Policy"
)

// KeyValueMap extends builtin map to support setting and getting
// select fields like BucketName, ObjectName, Prefix, etc.
type KeyValueMap map[string]string

// Bucket returns the BucketName
func (km KeyValueMap) Bucket() string {
return km[kmBucket]
}

// Object returns the ObjectName
func (km KeyValueMap) Object() string {
return km[kmObject]
}

// Prefix returns the Prefix
func (km KeyValueMap) Prefix() string {
return km[kmPrefix]
}

// Username returns the Username
func (km KeyValueMap) Username() string {
return km[kmUsername]
}

// Hostname returns the Hostname
func (km KeyValueMap) Hostname() string {
return km[kmHostname]
}

// Policy returns the Policy
func (km KeyValueMap) Policy() string {
return km[kmPolicy]
}

// Objects returns the Objects
func (km KeyValueMap) Objects() []string {
var objects []string
_ = json.Unmarshal([]byte(km[kmObjects]), &objects)
return objects
}

// SetBucket sets the given bucket to the KeyValueMap
func (km *KeyValueMap) SetBucket(bucket string) {
(*km)[kmBucket] = bucket
}

// SetPrefix sets the given prefix to the KeyValueMap
func (km *KeyValueMap) SetPrefix(prefix string) {
(*km)[kmPrefix] = prefix
}

// SetObject sets the given object to the KeyValueMap
func (km *KeyValueMap) SetObject(object string) {
(*km)[kmObject] = object
}

// SetMarker sets the given marker to the KeyValueMap
func (km *KeyValueMap) SetMarker(marker string) {
(*km)[kmMarker] = marker
}

// SetPolicy sets the given policy to the KeyValueMap
func (km *KeyValueMap) SetPolicy(policy string) {
(*km)[kmPolicy] = policy
}

// SetExpiry sets the expiry to the KeyValueMap
func (km *KeyValueMap) SetExpiry(expiry int64) {
(*km)[kmPolicy] = fmt.Sprintf("%d", expiry)
}

// SetObjects sets the list of objects to the KeyValueMap
func (km *KeyValueMap) SetObjects(objects []string) {
objsVal, err := json.Marshal(objects)
if err != nil {
// NB this can only happen when we can't marshal a Go
// slice to its json representation.
objsVal = []byte("[]")
}
(*km)[kmObjects] = string(objsVal)
}

// SetUsername sets the username to the KeyValueMap
func (km *KeyValueMap) SetUsername(username string) {
(*km)[kmUsername] = username
}

// SetHostname sets the hostname to the KeyValueMap
func (km *KeyValueMap) SetHostname(hostname string) {
(*km)[kmHostname] = hostname
}

// ToKeyValuer interface wraps ToKeyValue method that allows types to
// marshal their values as a map of structure member names to their
// values, as strings
type ToKeyValuer interface {
ToKeyValue() KeyValueMap
}

// ToKeyValue implementation for WebGenericArgs
func (args *WebGenericArgs) ToKeyValue() KeyValueMap {
return KeyValueMap{}
}

// ToKeyValue implementation for MakeBucketArgs
func (args *MakeBucketArgs) ToKeyValue() KeyValueMap {
km := KeyValueMap{}
km.SetBucket(args.BucketName)
return km
}

// ToKeyValue implementation for RemoveBucketArgs
func (args *RemoveBucketArgs) ToKeyValue() KeyValueMap {
km := KeyValueMap{}
km.SetBucket(args.BucketName)
return km
}

// ToKeyValue implementation for ListObjectsArgs
func (args *ListObjectsArgs) ToKeyValue() KeyValueMap {
km := KeyValueMap{}
km.SetBucket(args.BucketName)
km.SetPrefix(args.Prefix)
km.SetMarker(args.Marker)
return km
}

// ToKeyValue implementation for RemoveObjectArgs
func (args *RemoveObjectArgs) ToKeyValue() KeyValueMap {
km := KeyValueMap{}
km.SetBucket(args.BucketName)
km.SetObjects(args.Objects)
return km
}

// ToKeyValue implementation for LoginArgs
func (args *LoginArgs) ToKeyValue() KeyValueMap {
km := KeyValueMap{}
km.SetUsername(args.Username)
return km
}

// ToKeyValue implementation for GetBucketPolicyArgs
func (args *GetBucketPolicyArgs) ToKeyValue() KeyValueMap {
km := KeyValueMap{}
km.SetBucket(args.BucketName)
km.SetPrefix(args.Prefix)
return km
}

// ToKeyValue implementation for ListAllBucketPoliciesArgs
func (args *ListAllBucketPoliciesArgs) ToKeyValue() KeyValueMap {
km := KeyValueMap{}
km.SetBucket(args.BucketName)
return km
}

// ToKeyValue implementation for SetBucketPolicyWebArgs
func (args *SetBucketPolicyWebArgs) ToKeyValue() KeyValueMap {
km := KeyValueMap{}
km.SetBucket(args.BucketName)
km.SetPrefix(args.Prefix)
km.SetPolicy(args.Policy)
return km
}

// ToKeyValue implementation for SetAuthArgs
// SetAuthArgs doesn't implement the ToKeyValue interface that will be
// used by logger subsystem down the line, to avoid leaking
// credentials to an external log target
func (args *SetAuthArgs) ToKeyValue() KeyValueMap {
return KeyValueMap{}
}

// ToKeyValue implementation for PresignedGetArgs
func (args *PresignedGetArgs) ToKeyValue() KeyValueMap {
km := KeyValueMap{}
km.SetHostname(args.HostName)
km.SetBucket(args.BucketName)
km.SetObject(args.ObjectName)
km.SetExpiry(args.Expiry)
return km
}

// newWebContext creates a context with ReqInfo values from the given
// http request and api name.
func newWebContext(r *http.Request, args ToKeyValuer, api string) context.Context {
argsMap := args.ToKeyValue()
bucket := argsMap.Bucket()
object := argsMap.Object()
prefix := argsMap.Prefix()

if prefix != "" {
object = prefix
}
reqInfo := &logger.ReqInfo{
DeploymentID: globalDeploymentID,
RemoteHost: handlers.GetSourceIP(r),
UserAgent: r.UserAgent(),
API: api,
BucketName: bucket,
ObjectName: object,
}
return logger.SetReqInfo(context.Background(), reqInfo)
}
111 changes: 111 additions & 0 deletions cmd/web-handler-context_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* MinIO Cloud Storage, (C) 2019 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 cmd

import (
"bytes"
"net/http"
"testing"

"github.com/minio/minio/cmd/logger"
)

func TestKeyValueMap(t *testing.T) {
bucket := "bucket"
object := "object"
prefix := "prefix"
username := "username"
policy := "policy"
host := "min.io"
objects := []string{object, object}

km := KeyValueMap{}
km.SetBucket(bucket)
km.SetPrefix(prefix)
km.SetUsername(username)
km.SetHostname(host)
km.SetObject(object)
km.SetObjects(objects)
km.SetPolicy(policy)

if got := km.Bucket(); got != bucket {
t.Errorf("Expected %s but got %s", bucket, got)
}

if got := km.Object(); got != object {
t.Errorf("Expected %s but got %s", object, got)
}

areEqualObjects := func(as, bs []string) bool {
if len(as) != len(bs) {
return false
}

for i, a := range as {
b := bs[i]
if a != b {
return false
}
}
return true
}

if got := km.Objects(); !areEqualObjects(got, objects) {
t.Errorf("Expected %s but got %s", objects, got)
}

if got := km.Policy(); got != policy {
t.Errorf("Expected %s but got %s", policy, got)
}

if got := km.Prefix(); got != prefix {
t.Errorf("Expected %s but got %s", prefix, got)
}

if got := km.Username(); got != username {
t.Errorf("Expected %s but got %s", username, got)
}

if got := km.Hostname(); got != host {
t.Errorf("Expected %s but got %s", host, got)
}
}

func TestNewWebContext(t *testing.T) {
api := "Test API"
args := ListObjectsArgs{
BucketName: "bucket",
Prefix: "prefix",
Marker: "marker",
}

req, err := http.NewRequest(http.MethodPost, "http://min.io", bytes.NewReader([]byte("nothing")))
if err != nil {
t.Fatal("Unexpected failure while creating a test request")
}

ctx := newWebContext(req, &args, api)
reqInfo := logger.GetReqInfo(ctx)

if reqInfo.API != api {
t.Errorf("Expected %s got %s", api, reqInfo.API)
}

if reqInfo.BucketName != args.BucketName {
t.Errorf("Expected %s got %s", args.BucketName, reqInfo.BucketName)
}
}
Loading

0 comments on commit ffd7b70

Please sign in to comment.