Refactoring minio server command and flags

This commit is contained in:
Harshavardhana 2015-08-20 13:05:47 -07:00
parent 8463040cd1
commit 74587886d2
11 changed files with 429 additions and 276 deletions

View File

@ -1,4 +1,4 @@
all: getdeps install all: install
checkdeps: checkdeps:
@echo "Checking deps:" @echo "Checking deps:"

View File

@ -16,181 +16,12 @@
package main package main
import ( import "github.com/minio/minio/internal/github.com/minio/cli"
"os"
"path/filepath"
"github.com/minio/minio/internal/github.com/minio/cli" // Collection of minio commands currently supported are
"github.com/minio/minio/pkg/controller" var commands = []cli.Command{}
"github.com/minio/minio/pkg/donut"
"github.com/minio/minio/pkg/server"
"github.com/minio/minio/pkg/server/api"
)
var commands = []cli.Command{ // registerCommand registers a cli command
serverCmd, func registerCommand(command cli.Command) {
controllerCmd, commands = append(commands, command)
donutCmd,
}
var serverCmd = cli.Command{
Name: "server",
Description: "Server mode",
Action: runServer,
CustomHelpTemplate: `NAME:
minio {{.Name}} - {{.Description}}
USAGE:
minio {{.Name}}
EXAMPLES:
1. Start in server mode
$ minio {{.Name}}
`,
}
var controllerCmd = cli.Command{
Name: "controller",
Description: "Control mode",
Action: runController,
CustomHelpTemplate: `NAME:
minio {{.Name}} - {{.Description}}
USAGE:
minio {{.Name}}
EXAMPLES:
1. Get disks from controller
$ minio {{.Name}} disks http://localhost:9001/rpc
2. Get memstats from controller
$ minio {{.Name}} mem http://localhost:9001/rpc
`,
}
var donutSubCommands = []cli.Command{
{
Name: "make",
Description: "make a donut",
Action: runMkdonut,
CustomHelpTemplate: `NAME:
minio donut {{.Name}} - {{.Description}}
USAGE:
minio donut {{.Name}} DONUTNAME [DISKS...]
EXAMPLES:
1. Make a donut with 4 exports
$ minio donut {{.Name}} mongodb-backup /mnt/export1 /mnt/export2 /mnt/export3 /mnt/export4
2. Make a donut with 16 exports
$ minio donut {{.Name}} operational-data /mnt/export1 /mnt/export2 /mnt/export3 /mnt/export4 /mnt/export5 \
/mnt/export6 /mnt/export7 /mnt/export8 /mnt/export9 /mnt/export10 /mnt/export11 \
/mnt/export12 /mnt/export13 /mnt/export14 /mnt/export15 /mnt/export16
`,
},
}
var donutCmd = cli.Command{
Name: "donut",
Description: "Donut maker",
Subcommands: donutSubCommands,
}
func runMkdonut(c *cli.Context) {
if !c.Args().Present() || c.Args().First() == "help" {
cli.ShowCommandHelpAndExit(c, "make", 1)
}
donutName := c.Args().First()
if c.Args().First() != "" {
if !donut.IsValidDonut(donutName) {
Fatalf("Invalid donutname %s\n", donutName)
}
}
var disks []string
for _, disk := range c.Args().Tail() {
if _, err := isUsable(disk); err != nil {
Fatalln(err)
}
disks = append(disks, disk)
}
for _, disk := range disks {
if err := os.MkdirAll(filepath.Join(disk, donutName), 0700); err != nil {
Fatalln(err)
}
}
hostname, err := os.Hostname()
if err != nil {
Fatalln(err)
}
donutConfig := &donut.Config{}
donutConfig.Version = "0.0.1"
donutConfig.DonutName = donutName
donutConfig.NodeDiskMap = make(map[string][]string)
// keep it in exact order as it was specified, do not try to sort disks
donutConfig.NodeDiskMap[hostname] = disks
// default cache is unlimited
donutConfig.MaxSize = 512000000
if err := donut.SaveConfig(donutConfig); err != nil {
Fatalln(err)
}
Infoln("Success!")
}
func getServerConfig(c *cli.Context) api.Config {
certFile := c.GlobalString("cert")
keyFile := c.GlobalString("key")
if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
Fatalln("Both certificate and key are required to enable https.")
}
tls := (certFile != "" && keyFile != "")
return api.Config{
Address: c.GlobalString("address"),
TLS: tls,
CertFile: certFile,
KeyFile: keyFile,
RateLimit: c.GlobalInt("ratelimit"),
}
}
func runServer(c *cli.Context) {
if c.Args().Present() {
cli.ShowCommandHelpAndExit(c, "server", 1)
}
apiServerConfig := getServerConfig(c)
err := server.StartServices(apiServerConfig)
if err != nil {
Fatalln(err)
}
}
func runController(c *cli.Context) {
if len(c.Args()) < 2 || c.Args().First() == "help" {
cli.ShowCommandHelpAndExit(c, "controller", 1) // last argument is exit code
}
switch c.Args().First() {
case "mem":
memstats, err := controller.GetMemStats(c.Args().Tail().First())
if err != nil {
Fatalln(err)
}
Println(string(memstats))
case "sysinfo":
sysinfo, err := controller.GetSysInfo(c.Args().Tail().First())
if err != nil {
Fatalln(err)
}
Println(string(sysinfo))
case "auth":
keys, err := controller.GetAuthKeys(c.Args().Tail().First())
if err != nil {
Fatalln(err)
}
Println(string(keys))
}
} }

View File

@ -129,7 +129,7 @@ var (
mutex.Unlock() mutex.Unlock()
os.Exit(1) os.Exit(1)
default: default:
fmt.Print(a...) fmt.Println(a...)
} }
} }

74
controller-main.go Normal file
View File

@ -0,0 +1,74 @@
/*
* Minio Cloud Storage, (C) 2015 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 main
import (
"github.com/minio/minio/internal/github.com/minio/cli"
"github.com/minio/minio/pkg/controller"
)
var controllerCmd = cli.Command{
Name: "controller",
Usage: "Get|Set server configuration",
Action: controllerMain,
CustomHelpTemplate: `NAME:
minio {{.Name}} - {{.Description}}
USAGE:
minio {{.Name}} [get|set] [INFOTYPE] [SERVERURL]
EXAMPLES:
1. Get disks from controller
$ minio {{.Name}} get disks http://localhost:9001/rpc
2. Get memstats from controller
$ minio {{.Name}} get mem http://localhost:9001/rpc
`,
}
func controllerMain(c *cli.Context) {
if len(c.Args()) < 2 || c.Args().First() == "help" {
cli.ShowCommandHelpAndExit(c, "controller", 1) // last argument is exit code
}
if c.Args().First() == "get" {
newArgs := c.Args().Tail()
switch newArgs.First() {
case "mem":
memstats, err := controller.GetMemStats(newArgs.Tail().First())
if err != nil {
Fatalln(err)
}
Println(string(memstats))
case "sysinfo":
sysinfo, err := controller.GetSysInfo(newArgs.Tail().First())
if err != nil {
Fatalln(err)
}
Println(string(sysinfo))
case "auth":
keys, err := controller.GetAuthKeys(newArgs.Tail().First())
if err != nil {
Fatalln(err)
}
Println(string(keys))
}
}
if c.Args().First() == "set" {
Fatalln("Not supported yet")
}
}

99
donut-main.go Normal file
View File

@ -0,0 +1,99 @@
/*
* Minio Cloud Storage, (C) 2015 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 main
import (
"os"
"path/filepath"
"github.com/minio/minio/internal/github.com/minio/cli"
"github.com/minio/minio/pkg/donut"
)
var (
donutSubCommands = []cli.Command{
{
Name: "make",
Description: "make a donut",
Action: makeDonutMain,
CustomHelpTemplate: `NAME:
minio donut {{.Name}} - {{.Description}}
USAGE:
minio donut {{.Name}} DONUTNAME [DISKS...]
EXAMPLES:
1. Make a donut with 4 exports
$ minio donut {{.Name}} mongodb-backup /mnt/export1 /mnt/export2 /mnt/export3 /mnt/export4
2. Make a donut with 16 exports
$ minio donut {{.Name}} operational-data /mnt/export1 /mnt/export2 /mnt/export3 /mnt/export4 /mnt/export5 \
/mnt/export6 /mnt/export7 /mnt/export8 /mnt/export9 /mnt/export10 /mnt/export11 \
/mnt/export12 /mnt/export13 /mnt/export14 /mnt/export15 /mnt/export16
`,
},
}
donutCmd = cli.Command{
Name: "donut",
Usage: "Create and manage a donut configuration",
Subcommands: donutSubCommands,
}
)
func makeDonutMain(c *cli.Context) {
if !c.Args().Present() || c.Args().First() == "help" {
cli.ShowCommandHelpAndExit(c, "make", 1)
}
donutName := c.Args().First()
if c.Args().First() != "" {
if !donut.IsValidDonut(donutName) {
Fatalf("Invalid donutname %s\n", donutName)
}
}
var disks []string
for _, disk := range c.Args().Tail() {
if _, err := isUsable(disk); err != nil {
Fatalln(err)
}
disks = append(disks, disk)
}
for _, disk := range disks {
if err := os.MkdirAll(filepath.Join(disk, donutName), 0700); err != nil {
Fatalln(err)
}
}
hostname, err := os.Hostname()
if err != nil {
Fatalln(err)
}
donutConfig := &donut.Config{}
donutConfig.Version = "0.0.1"
donutConfig.DonutName = donutName
donutConfig.NodeDiskMap = make(map[string][]string)
// keep it in exact order as it was specified, do not try to sort disks
donutConfig.NodeDiskMap[hostname] = disks
// default cache is unlimited
donutConfig.MaxSize = 512000000
if err := donut.SaveConfig(donutConfig); err != nil {
Fatalln(err)
}
Infoln("Success!")
}

63
flags.go Normal file
View File

@ -0,0 +1,63 @@
/*
* Minio Cloud Storage, (C) 2015 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 main
import "github.com/minio/minio/internal/github.com/minio/cli"
// Collection of minio flags currently supported
var flags = []cli.Flag{}
var (
addressFlag = cli.StringFlag{
Name: "address",
Value: ":9000",
Usage: "ADDRESS:PORT for cloud storage access",
}
addressMgmtFlag = cli.StringFlag{
Name: "address-mgmt",
Hide: true,
Value: ":9001",
Usage: "ADDRESS:PORT for management console access",
}
ratelimitFlag = cli.IntFlag{
Name: "ratelimit",
Value: 16,
Usage: "Limit for total concurrent requests: [DEFAULT: 16]",
}
certFlag = cli.StringFlag{
Name: "cert",
Usage: "Provide your domain certificate",
}
keyFlag = cli.StringFlag{
Name: "key",
Usage: "Provide your domain private key",
}
debugFlag = cli.BoolFlag{
Name: "debug",
Usage: "print debug information",
}
)
// registerFlag registers a cli flag
func registerFlag(flag cli.Flag) {
flags = append(flags, flag)
}

View File

@ -20,6 +20,7 @@ package main
import ( import (
"fmt" "fmt"
"net/http"
"os" "os"
"text/template" "text/template"
"time" "time"
@ -27,37 +28,16 @@ import (
type Version struct { type Version struct {
Date string Date string
Tag string
} }
func writeVersion(version Version) error { func writeVersion(version Version) error {
var versionTemplate = `// -------- DO NOT EDIT -------- var versionTemplate = `// -------- DO NOT EDIT --------
// this is an autogenerated file // This file is autogenerated by genversion.go during the release process.
package main package main
import (
"net/http"
"time"
)
// Version autogenerated // Version autogenerated
var Version = {{if .Date}}"{{.Date}}"{{else}}""{{end}} const Version = {{if .Date}}"{{.Date}}"{{else}}""{{end}}
// Tag is of following format
//
// RELEASE.[WeekDay]-[Month]-[Day]-[Hour]-[Min]-[Sec]-GMT-[Year]
//
var Tag = {{if .Tag}}"{{.Tag}}"{{else}}""{{end}}
// getVersion -
func getVersion() string {
t, _ := time.Parse(time.RFC3339Nano, Version)
if t.IsZero() {
return ""
}
return t.Format(http.TimeFormat)
}
` `
t := template.Must(template.New("version").Parse(versionTemplate)) t := template.Must(template.New("version").Parse(versionTemplate))
versionFile, err := os.OpenFile("version.go", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) versionFile, err := os.OpenFile("version.go", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
@ -72,9 +52,13 @@ func getVersion() string {
return nil return nil
} }
func main() { func genVersion() {
t := time.Now().UTC() t := time.Now().UTC()
date := t.Format(time.RFC3339Nano) date := t.Format(time.RFC3339Nano)
// Tag is of following format
//
// RELEASE.[WeekDay]-[Month]-[Day]-[Hour]-[Min]-[Sec]-GMT-[Year]
//
tag := fmt.Sprintf( tag := fmt.Sprintf(
"RELEASE.%s-%s-%02d-%02d-%02d-%02d-GMT-%d", "RELEASE.%s-%s-%02d-%02d-%02d-%02d-GMT-%d",
t.Weekday().String()[0:3], t.Weekday().String()[0:3],
@ -83,12 +67,18 @@ func main() {
t.Hour(), t.Hour(),
t.Minute(), t.Minute(),
t.Second(), t.Second(),
t.Year(), t.Year())
) fmt.Println("Release-Tag: " + tag)
version := Version{Date: date, Tag: tag} fmt.Println("Release-Version: " + t.Format(http.TimeFormat))
version := Version{Date: date}
err := writeVersion(version) err := writeVersion(version)
if err != nil { if err != nil {
fmt.Print(err) fmt.Print(err)
os.Exit(1) os.Exit(1)
} }
fmt.Println("Successfully generated version.go")
}
func main() {
genVersion()
} }

99
main.go
View File

@ -18,10 +18,12 @@ package main
import ( import (
"fmt" "fmt"
"net/http"
"os" "os"
"os/user" "os/user"
"runtime" "runtime"
"strconv" "strconv"
"time"
"github.com/minio/minio/internal/github.com/dustin/go-humanize" "github.com/minio/minio/internal/github.com/dustin/go-humanize"
"github.com/minio/minio/internal/github.com/minio/cli" "github.com/minio/minio/internal/github.com/minio/cli"
@ -29,37 +31,6 @@ import (
var globalDebugFlag = false var globalDebugFlag = false
var flags = []cli.Flag{
cli.StringFlag{
Name: "address",
Value: ":9000",
Usage: "ADDRESS:PORT for cloud storage access",
},
cli.StringFlag{
Name: "address-mgmt",
Hide: true,
Value: ":9001",
Usage: "ADDRESS:PORT for management console access",
},
cli.IntFlag{
Name: "ratelimit",
Value: 16,
Usage: "Limit for total concurrent requests: [DEFAULT: 16]",
},
cli.StringFlag{
Name: "cert",
Usage: "Provide your domain certificate",
},
cli.StringFlag{
Name: "key",
Usage: "Provide your domain private key",
},
cli.BoolFlag{
Name: "debug",
Usage: "print debug information",
},
}
func init() { func init() {
// Check for the environment early on and gracefuly report. // Check for the environment early on and gracefuly report.
_, err := user.Current() _, err := user.Current()
@ -100,29 +71,39 @@ func init() {
} }
} }
func main() { // getFormattedVersion -
// set up go max processes func getFormattedVersion() string {
runtime.GOMAXPROCS(runtime.NumCPU()) t, _ := time.Parse(time.RFC3339Nano, Version)
if t.IsZero() {
return ""
}
return t.Format(http.TimeFormat)
}
func registerApp() *cli.App {
// register all commands
registerCommand(donutCmd)
registerCommand(serverCmd)
registerCommand(controllerCmd)
registerCommand(versionCmd)
// register all flags
registerFlag(addressFlag)
registerFlag(addressMgmtFlag)
registerFlag(ratelimitFlag)
registerFlag(certFlag)
registerFlag(keyFlag)
registerFlag(debugFlag)
// set up app // set up app
app := cli.NewApp() app := cli.NewApp()
app.Name = "minio" app.Name = "minio"
app.Version = getVersion() // hide --version flag, version is a command
app.Compiled = getVersion() app.HideVersion = true
app.Author = "Minio.io" app.Author = "Minio.io"
app.Usage = "Minio Cloud Storage" app.Usage = "Minio Cloud Storage"
app.Flags = flags app.Flags = flags
app.Commands = commands app.Commands = commands
app.Before = func(c *cli.Context) error {
globalDebugFlag = c.GlobalBool("debug")
return nil
}
app.ExtraInfo = func() map[string]string {
if globalDebugFlag {
return getSystemData()
}
return make(map[string]string)
}
app.CustomAppHelpTemplate = `NAME: app.CustomAppHelpTemplate = `NAME:
{{.Name}} - {{.Usage}} {{.Name}} - {{.Usage}}
@ -137,8 +118,9 @@ GLOBAL FLAGS:
{{range .Flags}}{{.}} {{range .Flags}}{{.}}
{{end}}{{end}} {{end}}{{end}}
VERSION: VERSION:
{{if .Compiled}}
{{.Compiled}}{{end}} ` + getFormattedVersion() +
`
{{range $key, $value := ExtraInfo}} {{range $key, $value := ExtraInfo}}
{{$key}}: {{$key}}:
{{$value}} {{$value}}
@ -149,5 +131,26 @@ VERSION:
Fatalf("Command not found: %s\n", command) Fatalf("Command not found: %s\n", command)
} }
return app
}
func registerBefore(c *cli.Context) error {
globalDebugFlag = c.GlobalBool("debug")
return nil
}
func main() {
// set up go max processes
runtime.GOMAXPROCS(runtime.NumCPU())
app := registerApp()
app.Before = registerBefore
app.ExtraInfo = func() map[string]string {
if globalDebugFlag {
return getSystemData()
}
return make(map[string]string)
}
app.RunAndExitOnError() app.RunAndExitOnError()
} }

67
server-main.go Normal file
View File

@ -0,0 +1,67 @@
/*
* Minio Cloud Storage, (C) 2015 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 main
import (
"github.com/minio/minio/internal/github.com/minio/cli"
"github.com/minio/minio/pkg/server"
"github.com/minio/minio/pkg/server/api"
)
var serverCmd = cli.Command{
Name: "server",
Usage: "Start minio server",
Action: serverMain,
CustomHelpTemplate: `NAME:
minio {{.Name}} - {{.Description}}
USAGE:
minio {{.Name}}
EXAMPLES:
1. Start minio server
$ minio {{.Name}}
`,
}
func getServerConfig(c *cli.Context) api.Config {
certFile := c.GlobalString("cert")
keyFile := c.GlobalString("key")
if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
Fatalln("Both certificate and key are required to enable https.")
}
tls := (certFile != "" && keyFile != "")
return api.Config{
Address: c.GlobalString("address"),
TLS: tls,
CertFile: certFile,
KeyFile: keyFile,
RateLimit: c.GlobalInt("ratelimit"),
}
}
func serverMain(c *cli.Context) {
if c.Args().Present() {
cli.ShowCommandHelpAndExit(c, "server", 1)
}
apiServerConfig := getServerConfig(c)
err := server.StartServices(apiServerConfig)
if err != nil {
Fatalln(err)
}
}

48
version-main.go Normal file
View File

@ -0,0 +1,48 @@
/*
* Minio Cloud Storage, (C) 2015 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 main
import (
"net/http"
"time"
"github.com/minio/minio/internal/github.com/minio/cli"
)
var versionCmd = cli.Command{
Name: "version",
Usage: "Print version",
Action: mainVersion,
CustomHelpTemplate: `NAME:
mc {{.Name}} - {{.Usage}}
USAGE:
mc {{.Name}} {{if .Description}}
EXAMPLES:
`,
}
func mainVersion(ctxx *cli.Context) {
t, _ := time.Parse(time.RFC3339Nano, Version)
if t.IsZero() {
Println("")
return
}
Println(t.Format(http.TimeFormat))
}

View File

@ -1,29 +1,7 @@
// -------- DO NOT EDIT -------- // -------- DO NOT EDIT --------
// this is an autogenerated file // This file is autogenerated by genversion.go during the release process.
package main package main
import (
"net/http"
"time"
)
// Version autogenerated // Version autogenerated
var Version = "2015-06-17T03:17:23.789648634Z" const Version = "2015-08-14T03:23:47.250240049Z"
// Tag is of following format
//
// [[STRING]-[EPOCH]
//
// STRING is release string of your choice.
// EPOCH is unix seconds since Jan 1, 1970 UTC.
var Tag = "release-1434511043"
// getVersion -
func getVersion() string {
t, _ := time.Parse(time.RFC3339Nano, Version)
if t.IsZero() {
return ""
}
return t.Format(http.TimeFormat)
}