Merge pull request #885 from harshavardhana/json-output

Add --json output formatter for server
master
Harshavardhana 9 years ago
commit f9174632bb
  1. 56
      controller-main.go
  2. 8
      controller-rpc.go
  3. 19
      flags.go
  4. 6
      main.go
  5. 4
      notifier.go
  6. 14
      pkg/donut/donut-v2.go
  7. 3
      pkg/donut/multipart.go
  8. 6
      server-auth-config.go
  9. 30
      server-auth.go
  10. 4
      server_auth_test.go
  11. 12
      server_donut_test.go
  12. 16
      server_signature_v4_test.go

@ -18,6 +18,8 @@ package main
import ( import (
"crypto/tls" "crypto/tls"
"encoding/json"
"fmt"
"net" "net"
"net/http" "net/http"
"os" "os"
@ -42,7 +44,7 @@ EXAMPLES:
1. Start minio controller 1. Start minio controller
$ minio {{.Name}} $ minio {{.Name}}
2. Colored output of generated keys 2. Fetch stored access keys
$ minio {{.Name}} keys $ minio {{.Name}} keys
`, `,
} }
@ -117,18 +119,16 @@ func genAuthFirstTime() (*AuthConfig, *probe.Error) {
config := &AuthConfig{} config := &AuthConfig{}
config.Version = "0.0.1" config.Version = "0.0.1"
config.Users = make(map[string]*AuthUser) config.Users = make(map[string]*AuthUser)
accessKeyID, err := GenerateAccessKeyID()
if err != nil {
return nil, err.Trace()
}
secretAccessKey, err := GenerateSecretAccessKey()
if err != nil {
return nil, err.Trace()
}
config.Users["admin"] = &AuthUser{ config.Users["admin"] = &AuthUser{
Name: "admin", Name: "admin",
AccessKeyID: string(accessKeyID), AccessKeyID: "admin",
SecretAccessKey: string(secretAccessKey), SecretAccessKey: string(mustGenerateSecretAccessKey()),
}
config.Users["user"] = &AuthUser{
Name: "user",
AccessKeyID: string(mustGenerateAccessKeyID()),
SecretAccessKey: string(mustGenerateSecretAccessKey()),
} }
if err := SaveConfig(config); err != nil { if err := SaveConfig(config); err != nil {
return nil, err.Trace() return nil, err.Trace()
@ -144,6 +144,21 @@ func getAuth() (*AuthConfig, *probe.Error) {
return config, nil return config, nil
} }
type accessKeys struct {
*AuthUser
}
func (a accessKeys) String() string {
return colorizeMessage(fmt.Sprintf("Username: %s, AccessKey: %s, SecretKey: %s", a.Name, a.AccessKeyID, a.SecretAccessKey))
}
// JSON - json formatted output
func (a accessKeys) JSON() string {
b, err := json.Marshal(a)
errorIf(probe.NewError(err), "Unable to marshal json", nil)
return string(b)
}
// firstTimeAuth first time authorization // firstTimeAuth first time authorization
func firstTimeAuth() *probe.Error { func firstTimeAuth() *probe.Error {
conf, err := genAuthFirstTime() conf, err := genAuthFirstTime()
@ -151,11 +166,17 @@ func firstTimeAuth() *probe.Error {
return err.Trace() return err.Trace()
} }
if conf != nil { if conf != nil {
Println("Running for first time, generating access keys.")
for _, user := range conf.Users { for _, user := range conf.Users {
Println(colorizeMessage("AccessKey: " + user.AccessKeyID)) if globalJSONFlag {
Println(colorizeMessage("SecretKey: " + user.SecretAccessKey)) Println(accessKeys{user}.JSON())
Println(colorizeMessage("$ minio controller keys")) } else {
Println(accessKeys{user})
} }
}
Println("To fetch your keys again.")
Println(" $ minio controller keys")
} }
return nil return nil
} }
@ -186,8 +207,11 @@ func controllerMain(c *cli.Context) {
fatalIf(err.Trace(), "Failed to fetch keys for minio controller.", nil) fatalIf(err.Trace(), "Failed to fetch keys for minio controller.", nil)
if conf != nil { if conf != nil {
for _, user := range conf.Users { for _, user := range conf.Users {
Println(colorizeMessage("AccessKey: " + user.AccessKeyID)) if globalJSONFlag {
Println(colorizeMessage("SecretKey: " + user.SecretAccessKey)) Println(accessKeys{user}.JSON())
} else {
Println(accessKeys{user})
}
} }
} }
return return

@ -49,13 +49,13 @@ func generateAuth(args *AuthArgs, reply *AuthRep) *probe.Error {
if _, ok := config.Users[args.User]; ok { if _, ok := config.Users[args.User]; ok {
return probe.NewError(errors.New("Credentials already set, if you wish to change this invoke Reset() method")) return probe.NewError(errors.New("Credentials already set, if you wish to change this invoke Reset() method"))
} }
accessKeyID, err := GenerateAccessKeyID() accessKeyID, err := generateAccessKeyID()
if err != nil { if err != nil {
return err.Trace() return err.Trace()
} }
reply.AccessKeyID = string(accessKeyID) reply.AccessKeyID = string(accessKeyID)
secretAccessKey, err := GenerateSecretAccessKey() secretAccessKey, err := generateSecretAccessKey()
if err != nil { if err != nil {
return err.Trace() return err.Trace()
} }
@ -97,12 +97,12 @@ func resetAuth(args *AuthArgs, reply *AuthRep) *probe.Error {
if _, ok := config.Users[args.User]; !ok { if _, ok := config.Users[args.User]; !ok {
return probe.NewError(errors.New("User not found")) return probe.NewError(errors.New("User not found"))
} }
accessKeyID, err := GenerateAccessKeyID() accessKeyID, err := generateAccessKeyID()
if err != nil { if err != nil {
return err.Trace() return err.Trace()
} }
reply.AccessKeyID = string(accessKeyID) reply.AccessKeyID = string(accessKeyID)
secretAccessKey, err := GenerateSecretAccessKey() secretAccessKey, err := generateSecretAccessKey()
if err != nil { if err != nil {
return err.Trace() return err.Trace()
} }

@ -25,42 +25,43 @@ var (
addressFlag = cli.StringFlag{ addressFlag = cli.StringFlag{
Name: "address", Name: "address",
Value: ":9000", Value: ":9000",
Usage: "ADDRESS:PORT for cloud storage access", Usage: "ADDRESS:PORT for cloud storage access.",
} }
addressControllerFlag = cli.StringFlag{ addressControllerFlag = cli.StringFlag{
Name: "address-controller", Name: "address-controller",
Hide: true, Hide: true,
Value: ":9001", Value: ":9001",
Usage: "ADDRESS:PORT for management console access", Usage: "ADDRESS:PORT for management console access.",
} }
addressServerRPCFlag = cli.StringFlag{ addressServerRPCFlag = cli.StringFlag{
Name: "address-server-rpc", Name: "address-server-rpc",
Hide: true, Hide: true,
Value: ":9002", Value: ":9002",
Usage: "ADDRESS:PORT for management console access", Usage: "ADDRESS:PORT for management console access.",
} }
ratelimitFlag = cli.IntFlag{ ratelimitFlag = cli.IntFlag{
Name: "ratelimit", Name: "ratelimit",
Hide: true,
Value: 16, Value: 16,
Usage: "Limit for total concurrent requests: [DEFAULT: 16]", Usage: "Limit for total concurrent requests: [DEFAULT: 16].",
} }
certFlag = cli.StringFlag{ certFlag = cli.StringFlag{
Name: "cert", Name: "cert",
Usage: "Provide your domain certificate", Usage: "Provide your domain certificate.",
} }
keyFlag = cli.StringFlag{ keyFlag = cli.StringFlag{
Name: "key", Name: "key",
Usage: "Provide your domain private key", Usage: "Provide your domain private key.",
} }
debugFlag = cli.BoolFlag{ jsonFlag = cli.BoolFlag{
Name: "debug", Name: "json",
Usage: "print debug information", Usage: "Enable json formatted output.",
} }
) )

@ -98,11 +98,11 @@ func registerApp() *cli.App {
// register all flags // register all flags
registerFlag(addressFlag) registerFlag(addressFlag)
registerFlag(addressControllerFlag) registerFlag(addressControllerFlag)
registerFlag(addressServerRPCFlag)
registerFlag(ratelimitFlag) registerFlag(ratelimitFlag)
registerFlag(certFlag) registerFlag(certFlag)
registerFlag(keyFlag) registerFlag(keyFlag)
registerFlag(debugFlag) registerFlag(jsonFlag)
registerFlag(addressServerRPCFlag)
// set up app // set up app
app := cli.NewApp() app := cli.NewApp()
@ -151,7 +151,7 @@ VERSION:
func main() { func main() {
app := registerApp() app := registerApp()
app.Before = func(c *cli.Context) error { app.Before = func(c *cli.Context) error {
// get flag and set global defaults here. globalJSONFlag = c.GlobalBool("json")
return nil return nil
} }
app.ExtraInfo = func() map[string]string { app.ExtraInfo = func() map[string]string {

@ -42,8 +42,8 @@ func colorizeMessage(message string) string {
terminal, err := ts.GetSize() terminal, err := ts.GetSize()
if err != nil { if err != nil {
globalJSONFlag = true // no coloring needed just send as is
return "" return message
} }
var msg string var msg string
switch { switch {

@ -187,22 +187,20 @@ func (donut API) GetObject(w io.Writer, bucket string, object string, start, len
} }
return 0, probe.NewError(ObjectNotFound{Object: object}) return 0, probe.NewError(ObjectNotFound{Object: object})
} }
{
var err error var err error
if start == 0 && length == 0 { if start == 0 && length == 0 {
written, err = io.CopyN(w, bytes.NewBuffer(data), int64(donut.objects.Len(objectKey))) written, err = io.CopyN(w, bytes.NewBuffer(data), int64(donut.objects.Len(objectKey)))
if err != nil { if err != nil {
return 0, probe.NewError(err) return 0, probe.NewError(err)
} }
} else { return written, nil
}
written, err = io.CopyN(w, bytes.NewBuffer(data[start:]), length) written, err = io.CopyN(w, bytes.NewBuffer(data[start:]), length)
if err != nil { if err != nil {
return 0, probe.NewError(err) return 0, probe.NewError(err)
} }
}
return written, nil return written, nil
} }
}
// GetBucketMetadata - // GetBucketMetadata -
func (donut API) GetBucketMetadata(bucket string) (BucketMetadata, *probe.Error) { func (donut API) GetBucketMetadata(bucket string) (BucketMetadata, *probe.Error) {
@ -353,6 +351,7 @@ func (donut API) createObject(bucket, key, contentType, expectedMD5Sum string, s
var length int var length int
byteBuffer := make([]byte, 1024*1024) byteBuffer := make([]byte, 1024*1024)
length, err = data.Read(byteBuffer) length, err = data.Read(byteBuffer)
if length != 0 {
hash.Write(byteBuffer[0:length]) hash.Write(byteBuffer[0:length])
sha256hash.Write(byteBuffer[0:length]) sha256hash.Write(byteBuffer[0:length])
ok := donut.objects.Append(objectKey, byteBuffer[0:length]) ok := donut.objects.Append(objectKey, byteBuffer[0:length])
@ -362,6 +361,7 @@ func (donut API) createObject(bucket, key, contentType, expectedMD5Sum string, s
totalLength += int64(length) totalLength += int64(length)
go debug.FreeOSMemory() go debug.FreeOSMemory()
} }
}
if size != 0 { if size != 0 {
if totalLength != size { if totalLength != size {
// Delete perhaps the object is already saved, due to the nature of append() // Delete perhaps the object is already saved, due to the nature of append()
@ -377,15 +377,21 @@ func (donut API) createObject(bucket, key, contentType, expectedMD5Sum string, s
// Verify if the written object is equal to what is expected, only if it is requested as such // Verify if the written object is equal to what is expected, only if it is requested as such
if strings.TrimSpace(expectedMD5Sum) != "" { if strings.TrimSpace(expectedMD5Sum) != "" {
if err := isMD5SumEqual(strings.TrimSpace(expectedMD5Sum), md5Sum); err != nil { if err := isMD5SumEqual(strings.TrimSpace(expectedMD5Sum), md5Sum); err != nil {
// Delete perhaps the object is already saved, due to the nature of append()
donut.objects.Delete(objectKey)
return ObjectMetadata{}, probe.NewError(BadDigest{}) return ObjectMetadata{}, probe.NewError(BadDigest{})
} }
} }
if signature != nil { if signature != nil {
ok, err := signature.DoesSignatureMatch(hex.EncodeToString(sha256hash.Sum(nil))) ok, err := signature.DoesSignatureMatch(hex.EncodeToString(sha256hash.Sum(nil)))
if err != nil { if err != nil {
// Delete perhaps the object is already saved, due to the nature of append()
donut.objects.Delete(objectKey)
return ObjectMetadata{}, err.Trace() return ObjectMetadata{}, err.Trace()
} }
if !ok { if !ok {
// Delete perhaps the object is already saved, due to the nature of append()
donut.objects.Delete(objectKey)
return ObjectMetadata{}, probe.NewError(signv4.DoesNotMatch{}) return ObjectMetadata{}, probe.NewError(signv4.DoesNotMatch{})
} }
} }

@ -23,6 +23,7 @@ import (
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/xml" "encoding/xml"
"io" "io"
"io/ioutil" "io/ioutil"
"math/rand" "math/rand"
@ -190,6 +191,7 @@ func (donut API) createObjectPart(bucket, key, uploadID string, partID int, cont
var length int var length int
byteBuffer := make([]byte, 1024*1024) byteBuffer := make([]byte, 1024*1024)
length, err = data.Read(byteBuffer) // do not read error return error here, we will handle this error later length, err = data.Read(byteBuffer) // do not read error return error here, we will handle this error later
if length != 0 {
hash.Write(byteBuffer[0:length]) hash.Write(byteBuffer[0:length])
sha256hash.Write(byteBuffer[0:length]) sha256hash.Write(byteBuffer[0:length])
ok := donut.multiPartObjects[uploadID].Append(partID, byteBuffer[0:length]) ok := donut.multiPartObjects[uploadID].Append(partID, byteBuffer[0:length])
@ -199,6 +201,7 @@ func (donut API) createObjectPart(bucket, key, uploadID string, partID int, cont
totalLength += int64(length) totalLength += int64(length)
go debug.FreeOSMemory() go debug.FreeOSMemory()
} }
}
if totalLength != size { if totalLength != size {
donut.multiPartObjects[uploadID].Delete(partID) donut.multiPartObjects[uploadID].Delete(partID)
return "", probe.NewError(IncompleteBody{Bucket: bucket, Object: key}) return "", probe.NewError(IncompleteBody{Bucket: bucket, Object: key})

@ -27,9 +27,9 @@ import (
// AuthUser container // AuthUser container
type AuthUser struct { type AuthUser struct {
Name string Name string `json:"name"`
AccessKeyID string AccessKeyID string `json:"accessKeyId"`
SecretAccessKey string SecretAccessKey string `json:"secretAccessKey"`
} }
// AuthConfig auth keys // AuthConfig auth keys

@ -23,9 +23,9 @@ import (
"github.com/minio/minio/pkg/probe" "github.com/minio/minio/pkg/probe"
) )
// GenerateAccessKeyID - generate random alpha numeric value using only uppercase characters // generateAccessKeyID - generate random alpha numeric value using only uppercase characters
// takes input as size in integer // takes input as size in integer
func GenerateAccessKeyID() ([]byte, *probe.Error) { func generateAccessKeyID() ([]byte, *probe.Error) {
alpha := make([]byte, MinioAccessID) alpha := make([]byte, MinioAccessID)
_, err := rand.Read(alpha) _, err := rand.Read(alpha)
if err != nil { if err != nil {
@ -37,8 +37,8 @@ func GenerateAccessKeyID() ([]byte, *probe.Error) {
return alpha, nil return alpha, nil
} }
// GenerateSecretAccessKey - generate random base64 numeric value from a random seed. // generateSecretAccessKey - generate random base64 numeric value from a random seed.
func GenerateSecretAccessKey() ([]byte, *probe.Error) { func generateSecretAccessKey() ([]byte, *probe.Error) {
rb := make([]byte, MinioSecretID) rb := make([]byte, MinioSecretID)
_, err := rand.Read(rb) _, err := rand.Read(rb)
if err != nil { if err != nil {
@ -46,3 +46,25 @@ func GenerateSecretAccessKey() ([]byte, *probe.Error) {
} }
return []byte(base64.StdEncoding.EncodeToString(rb))[:MinioSecretID], nil return []byte(base64.StdEncoding.EncodeToString(rb))[:MinioSecretID], nil
} }
// mustGenerateAccessKeyID - must generate random alpha numeric value using only uppercase characters
// takes input as size in integer
func mustGenerateAccessKeyID() []byte {
alpha := make([]byte, MinioAccessID)
_, err := rand.Read(alpha)
fatalIf(probe.NewError(err), "Unable to get random number from crypto/rand.", nil)
for i := 0; i < MinioAccessID; i++ {
alpha[i] = alphaNumericTable[alpha[i]%byte(len(alphaNumericTable))]
}
return alpha
}
// mustGenerateSecretAccessKey - generate random base64 numeric value from a random seed.
func mustGenerateSecretAccessKey() []byte {
rb := make([]byte, MinioSecretID)
_, err := rand.Read(rb)
fatalIf(probe.NewError(err), "Unable to get random number from crypto/rand.", nil)
return []byte(base64.StdEncoding.EncodeToString(rb))[:MinioSecretID]
}

@ -23,10 +23,10 @@ type MySuite struct{}
var _ = Suite(&MySuite{}) var _ = Suite(&MySuite{})
func (s *MySuite) TestAuth(c *C) { func (s *MySuite) TestAuth(c *C) {
secretID, err := GenerateSecretAccessKey() secretID, err := generateSecretAccessKey()
c.Assert(err, IsNil) c.Assert(err, IsNil)
accessID, err := GenerateAccessKeyID() accessID, err := generateAccessKeyID()
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(len(secretID), Equals, MinioSecretID) c.Assert(len(secretID), Equals, MinioSecretID)

@ -893,16 +893,4 @@ func (s *MyAPIDonutSuite) TestObjectMultipart(c *C) {
response, err = client.Do(request) response, err = client.Do(request)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(response.StatusCode, Equals, http.StatusOK) c.Assert(response.StatusCode, Equals, http.StatusOK)
/*
request, err = http.NewRequest("GET", testAPIDonutServer.URL+"/objectmultiparts/object", nil)
c.Assert(err, IsNil)
response, err = client.Do(request)
c.Assert(err, IsNil)
c.Assert(response.StatusCode, Equals, http.StatusOK)
object, err := ioutil.ReadAll(response.Body)
c.Assert(err, IsNil)
c.Assert(string(object), Equals, ("hello worldhello world"))
*/
} }

@ -66,9 +66,9 @@ func (s *MyAPISignatureV4Suite) SetUpSuite(c *C) {
perr := donut.SaveConfig(conf) perr := donut.SaveConfig(conf)
c.Assert(perr, IsNil) c.Assert(perr, IsNil)
accessKeyID, perr := GenerateAccessKeyID() accessKeyID, perr := generateAccessKeyID()
c.Assert(perr, IsNil) c.Assert(perr, IsNil)
secretAccessKey, perr := GenerateSecretAccessKey() secretAccessKey, perr := generateSecretAccessKey()
c.Assert(perr, IsNil) c.Assert(perr, IsNil)
authConf := &AuthConfig{} authConf := &AuthConfig{}
@ -1133,16 +1133,4 @@ func (s *MyAPISignatureV4Suite) TestObjectMultipart(c *C) {
response, err = client.Do(request) response, err = client.Do(request)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(response.StatusCode, Equals, http.StatusOK) c.Assert(response.StatusCode, Equals, http.StatusOK)
/*
request, err = s.newRequest("GET", testSignatureV4Server.URL+"/objectmultiparts/object", 0, nil)
c.Assert(err, IsNil)
response, err = client.Do(request)
c.Assert(err, IsNil)
c.Assert(response.StatusCode, Equals, http.StatusOK)
object, err := ioutil.ReadAll(response.Body)
c.Assert(err, IsNil)
c.Assert(string(object), Equals, ("hello worldhello world"))
*/
} }

Loading…
Cancel
Save