From 535bcc3eac8b6c9d387a02f893056c76ac3516f5 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Sun, 12 Jul 2015 18:45:19 -0700 Subject: [PATCH] Add mkdonut command --- cmd/mkdonut/console.go | 171 +++++++++++++++++++++++++++++++++++++++++ cmd/mkdonut/disks.go | 56 ++++++++++++++ cmd/mkdonut/main.go | 118 ++++++++++++++++++++++++++++ cmd/mkdonut/make.go | 130 +++++++++++++++++++++++++++++++ cmd/mkdonut/version.go | 21 +++++ console.go | 2 +- make.go | 22 +----- 7 files changed, 500 insertions(+), 20 deletions(-) create mode 100644 cmd/mkdonut/console.go create mode 100644 cmd/mkdonut/disks.go create mode 100644 cmd/mkdonut/main.go create mode 100644 cmd/mkdonut/make.go create mode 100644 cmd/mkdonut/version.go diff --git a/cmd/mkdonut/console.go b/cmd/mkdonut/console.go new file mode 100644 index 000000000..a6ef39808 --- /dev/null +++ b/cmd/mkdonut/console.go @@ -0,0 +1,171 @@ +/* + * Minimalist Object 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 ( + "fmt" + "os" + "sync" + + "path/filepath" +) + +type logLevel int + +const ( + levelUnknown logLevel = iota + levelPrint + levelDebug + levelInfo + levelError + levelFatal +) + +var ( + mutex = &sync.RWMutex{} + + // Fatal prints a error message and exits + Fatal = func(a ...interface{}) { privatePrint(levelFatal, a...) } + // Fatalln prints a error message with a new line and exits + Fatalln = func(a ...interface{}) { privatePrintln(levelFatal, a...) } + // Fatalf prints a error message with formatting and exits + Fatalf = func(f string, a ...interface{}) { privatePrintf(levelFatal, f, a...) } + + // Error prints a error message + Error = func(a ...interface{}) { privatePrint(levelError, a...) } + // Errorln prints a error message with a new line + Errorln = func(a ...interface{}) { privatePrintln(levelError, a...) } + // Errorf prints a error message with formatting + Errorf = func(f string, a ...interface{}) { privatePrintf(levelError, f, a...) } + + // Info prints a informational message + Info = func(a ...interface{}) { privatePrint(levelInfo, a...) } + // Infoln prints a informational message with a new line + Infoln = func(a ...interface{}) { privatePrintln(levelInfo, a...) } + // Infof prints a informational message with formatting + Infof = func(f string, a ...interface{}) { privatePrintf(levelInfo, f, a...) } + + // Debug prints a debug message + Debug = func(a ...interface{}) { privatePrint(levelDebug, a...) } + // Debugln prints a debug message with a new line + Debugln = func(a ...interface{}) { privatePrintln(levelDebug, a...) } + // Debugf prints a debug message with formatting + Debugf = func(f string, a ...interface{}) { privatePrintf(levelDebug, f, a...) } + + // Print prints a debug message + Print = func(a ...interface{}) { privatePrint(levelPrint, a...) } + // Println prints a debug message with a new line + Println = func(a ...interface{}) { privatePrintln(levelPrint, a...) } + // Printf prints a debug message with formatting + Printf = func(f string, a ...interface{}) { privatePrintf(levelPrint, f, a...) } +) + +var ( + // print prints a message prefixed with message type and program name + privatePrint = func(l logLevel, a ...interface{}) { + switch l { + case levelDebug: + mutex.Lock() + fmt.Fprint(os.Stderr, ProgramName()+": ") + fmt.Fprint(os.Stderr, a...) + mutex.Unlock() + case levelInfo: + mutex.Lock() + fmt.Print(ProgramName() + ": ") + fmt.Print(a...) + mutex.Unlock() + case levelError: + mutex.Lock() + fmt.Fprint(os.Stderr, ProgramName()+": ") + fmt.Fprint(os.Stderr, a...) + mutex.Unlock() + case levelFatal: + mutex.Lock() + fmt.Fprint(os.Stderr, ProgramName()+": ") + fmt.Fprint(os.Stderr, a...) + mutex.Unlock() + os.Exit(1) + default: + fmt.Print(a...) + } + } + + // println - same as print with a new line + privatePrintln = func(l logLevel, a ...interface{}) { + switch l { + case levelDebug: + mutex.Lock() + fmt.Fprint(os.Stderr, ProgramName()+": ") + fmt.Fprintln(os.Stderr, a...) + mutex.Unlock() + case levelInfo: + mutex.Lock() + fmt.Print(ProgramName() + ": ") + fmt.Println(a...) + mutex.Unlock() + case levelError: + mutex.Lock() + fmt.Fprint(os.Stderr, ProgramName()+": ") + fmt.Fprintln(os.Stderr, a...) + mutex.Unlock() + case levelFatal: + mutex.Lock() + fmt.Fprint(os.Stderr, ProgramName()+": ") + fmt.Fprintln(os.Stderr, a...) + mutex.Unlock() + os.Exit(1) + default: + fmt.Print(a...) + } + } + + // printf - same as print, but takes a format specifier + privatePrintf = func(l logLevel, f string, a ...interface{}) { + switch l { + case levelDebug: + mutex.Lock() + fmt.Fprint(os.Stderr, ProgramName()+": ") + fmt.Fprintf(os.Stderr, f, a...) + mutex.Unlock() + case levelInfo: + mutex.Lock() + fmt.Print(ProgramName() + ": ") + fmt.Printf(f, a...) + mutex.Unlock() + case levelError: + mutex.Lock() + fmt.Fprint(os.Stderr, ProgramName()+": ") + fmt.Fprintf(os.Stderr, f, a...) + mutex.Unlock() + + case levelFatal: + mutex.Lock() + fmt.Fprint(os.Stderr, ProgramName()+": ") + fmt.Fprintf(os.Stderr, f, a...) + mutex.Unlock() + os.Exit(1) + default: + fmt.Printf(f, a...) + } + } +) + +// ProgramName - return the name of the executable program +func ProgramName() string { + _, progName := filepath.Split(os.Args[0]) + return progName +} diff --git a/cmd/mkdonut/disks.go b/cmd/mkdonut/disks.go new file mode 100644 index 000000000..b98a5c022 --- /dev/null +++ b/cmd/mkdonut/disks.go @@ -0,0 +1,56 @@ +/* + * Minimalist Object 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 ( + "errors" + "io/ioutil" + "os" + "path/filepath" + "syscall" + + "github.com/minio/minio/pkg/iodine" +) + +// IsUsable provides a comprehensive way of knowing if the provided mountPath is mounted and writable +func IsUsable(mountPath string) (bool, error) { + mntpoint, err := os.Stat(mountPath) + if err != nil { + return false, iodine.New(err, nil) + } + parent, err := os.Stat(filepath.Join(mountPath, "..")) + if err != nil { + return false, iodine.New(err, nil) + } + mntpointSt := mntpoint.Sys().(*syscall.Stat_t) + parentSt := parent.Sys().(*syscall.Stat_t) + + if mntpointSt.Dev == parentSt.Dev { + return false, iodine.New(errors.New("not mounted"), nil) + } + testFile, err := ioutil.TempFile(mountPath, "writetest-") + if err != nil { + return false, iodine.New(err, nil) + } + testFileName := testFile.Name() + // close the file, to avoid leaky fd's + testFile.Close() + if err := os.Remove(testFileName); err != nil { + return false, iodine.New(err, nil) + } + return true, nil +} diff --git a/cmd/mkdonut/main.go b/cmd/mkdonut/main.go new file mode 100644 index 000000000..a9c033f2f --- /dev/null +++ b/cmd/mkdonut/main.go @@ -0,0 +1,118 @@ +/* + * Minimalist Object 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 ( + "fmt" + "os" + "os/user" + "runtime" + "strconv" + "time" + + "github.com/dustin/go-humanize" + "github.com/minio/cli" + "github.com/minio/minio/pkg/iodine" +) + +var globalDebugFlag = false + +var flags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug", + Usage: "print debug information", + }, +} + +func init() { + // Check for the environment early on and gracefuly report. + _, err := user.Current() + if err != nil { + Fatalf("Unable to obtain user's home directory. \nError: %s\n", err) + } +} + +// Tries to get os/arch/platform specific information +// Returns a map of current os/arch/platform/memstats +func getSystemData() map[string]string { + host, err := os.Hostname() + if err != nil { + host = "" + } + memstats := &runtime.MemStats{} + runtime.ReadMemStats(memstats) + mem := fmt.Sprintf("Used: %s | Allocated: %s | Used-Heap: %s | Allocated-Heap: %s", + humanize.Bytes(memstats.Alloc), + humanize.Bytes(memstats.TotalAlloc), + humanize.Bytes(memstats.HeapAlloc), + humanize.Bytes(memstats.HeapSys)) + platform := fmt.Sprintf("Host: %s | OS: %s | Arch: %s", + host, + runtime.GOOS, + runtime.GOARCH) + goruntime := fmt.Sprintf("Version: %s | CPUs: %s", runtime.Version(), strconv.Itoa(runtime.NumCPU())) + return map[string]string{ + "PLATFORM": platform, + "RUNTIME": goruntime, + "MEM": mem, + } +} + +func main() { + // set up iodine + iodine.SetGlobalState("mkdonut.version", Version) + iodine.SetGlobalState("mkdonut.starttime", time.Now().Format(time.RFC3339)) + + // set up go max processes + runtime.GOMAXPROCS(runtime.NumCPU()) + + // set up app + app := cli.NewApp() + app.Name = "minio" + app.Version = Version + app.Compiled = getVersion() + app.Author = "Minio.io" + app.Usage = "Make a donut" + app.Flags = flags + app.Before = func(c *cli.Context) error { + if c.GlobalBool("debug") { + app.ExtraInfo = getSystemData() + } + return nil + } + app.CustomAppHelpTemplate = `NAME: + {{.Name}} - {{.Usage}} + +USAGE: + {{.Name}} {{if .Flags}}[global flags] {{end}}command{{if .Flags}} [command flags]{{end}} [arguments...] + +COMMANDS: + {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} + {{end}}{{if .Flags}} +GLOBAL FLAGS: + {{range .Flags}}{{.}} + {{end}}{{end}} +VERSION: + {{if .Compiled}} + {{.Compiled}}{{end}} + {{range $key, $value := .ExtraInfo}} +{{$key}}: + {{$value}} +{{end}} +` + app.RunAndExitOnError() +} diff --git a/cmd/mkdonut/make.go b/cmd/mkdonut/make.go new file mode 100644 index 000000000..366ebc1fd --- /dev/null +++ b/cmd/mkdonut/make.go @@ -0,0 +1,130 @@ +// +build ignore + +/* + * Makefile alternative for Minimalist Object 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 ( + "bytes" + "fmt" + "os" + "os/exec" + "text/template" + "time" + + "github.com/minio/cli" +) + +type Version struct { + Date string +} + +func writeVersion(version Version) error { + var versionTemplate = `// -------- DO NOT EDIT -------- +// this is an autogenerated file + +package main + +import ( + "net/http" + "time" +) + +// Version autogenerated +var Version = {{if .Date}}"{{.Date}}"{{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)) + versionFile, err := os.OpenFile("version.go", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return err + } + defer versionFile.Close() + err = t.Execute(versionFile, version) + if err != nil { + return err + } + return nil +} + +type command struct { + cmd *exec.Cmd + stderr *bytes.Buffer + stdout *bytes.Buffer +} + +func (c command) runCommand() error { + c.cmd.Stdout = c.stdout + c.cmd.Stderr = c.stderr + return c.cmd.Run() +} +func (c command) String() string { + message := c.stderr.String() + message += c.stdout.String() + return message +} + +func runMkdonutInstall(ctx *cli.Context) { + if ctx.Args().First() == "help" { + cli.ShowCommandHelpAndExit(ctx, "install", 1) // last argument is exit code + } + mkdonutInstall := command{exec.Command("godep", "go", "install", "-a", "github.com/minio/minio/cmd/mkdonut"), &bytes.Buffer{}, &bytes.Buffer{}} + mkdonutInstallErr := mkdonutInstall.runCommand() + if mkdonutInstallErr != nil { + fmt.Println(mkdonutInstall) + os.Exit(1) + } + fmt.Print(mkdonutInstall) +} + +func runMkdonutRelease(ctx *cli.Context) { + if ctx.Args().First() == "help" { + cli.ShowCommandHelpAndExit(ctx, "release", 1) // last argument is exit code + } + t := time.Now().UTC() + date := t.Format(time.RFC3339Nano) + version := Version{Date: date} + err := writeVersion(version) + if err != nil { + fmt.Print(err) + os.Exit(1) + } +} + +func main() { + app := cli.NewApp() + app.Commands = []cli.Command{ + { + Name: "release", + Action: runMkdonutRelease, + }, + { + Name: "install", + Action: runMkdonutInstall, + }, + } + app.Author = "Minio.io" + app.RunAndExitOnError() +} diff --git a/cmd/mkdonut/version.go b/cmd/mkdonut/version.go new file mode 100644 index 000000000..17ca8c3d3 --- /dev/null +++ b/cmd/mkdonut/version.go @@ -0,0 +1,21 @@ +// -------- DO NOT EDIT -------- +// this is an autogenerated file + +package main + +import ( + "net/http" + "time" +) + +// Version autogenerated +var Version = "2015-07-13T01:59:53.151205631Z" + +// getVersion - +func getVersion() string { + t, _ := time.Parse(time.RFC3339Nano, Version) + if t.IsZero() { + return "" + } + return t.Format(http.TimeFormat) +} diff --git a/console.go b/console.go index 49daec834..a6ef39808 100644 --- a/console.go +++ b/console.go @@ -1,5 +1,5 @@ /* - * Mini Copy (C) 2015 Minio, Inc. + * Minimalist Object 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. diff --git a/make.go b/make.go index 102ed3c92..e831ab8a2 100644 --- a/make.go +++ b/make.go @@ -1,7 +1,7 @@ // +build ignore /* - * Minio Client (C) 2014, 2015 Minio, Inc. + * Makefile alternative for Minimalist Object 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. @@ -23,7 +23,6 @@ import ( "fmt" "os" "os/exec" - "strconv" "text/template" "time" @@ -49,14 +48,6 @@ import ( // Version autogenerated var Version = {{if .Date}}"{{.Date}}"{{else}}""{{end}} -// 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 = {{if .Tag}}"{{.Tag}}"{{else}}""{{end}} - // getVersion - func getVersion() string { t, _ := time.Parse(time.RFC3339Nano, Version) @@ -109,7 +100,7 @@ func runMinioInstall(ctx *cli.Context) { fmt.Print(minioGenerate) os.Exit(1) } - fmt.Println(minioGenerate) + fmt.Print(minioGenerate) minioBuildErr := minioBuild.runCommand() if minioBuildErr != nil { fmt.Print(minioBuild) @@ -136,13 +127,7 @@ func runMinioRelease(ctx *cli.Context) { } t := time.Now().UTC() date := t.Format(time.RFC3339Nano) - // [[STRING]-[EPOCH] - // - // STRING is release string of your choice. - // EPOCH is unix seconds since Jan 1, 1970 UTC. - tag := "release" + "-" + strconv.FormatInt(t.Unix(), 10) - - version := Version{Date: date, Tag: tag} + version := Version{Date: date} err := writeVersion(version) if err != nil { fmt.Print(err) @@ -152,7 +137,6 @@ func runMinioRelease(ctx *cli.Context) { func main() { app := cli.NewApp() - app.Usage = "Minimalist Object Storage" app.Commands = []cli.Command{ { Name: "release",