Add mkdonut command

This commit is contained in:
Harshavardhana 2015-07-12 18:45:19 -07:00
parent 4c95775864
commit 535bcc3eac
7 changed files with 500 additions and 20 deletions

171
cmd/mkdonut/console.go Normal file
View File

@ -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()+": <DEBUG> ")
fmt.Fprint(os.Stderr, a...)
mutex.Unlock()
case levelInfo:
mutex.Lock()
fmt.Print(ProgramName() + ": <INFO> ")
fmt.Print(a...)
mutex.Unlock()
case levelError:
mutex.Lock()
fmt.Fprint(os.Stderr, ProgramName()+": <ERROR> ")
fmt.Fprint(os.Stderr, a...)
mutex.Unlock()
case levelFatal:
mutex.Lock()
fmt.Fprint(os.Stderr, ProgramName()+": <FATAL> ")
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()+": <DEBUG> ")
fmt.Fprintln(os.Stderr, a...)
mutex.Unlock()
case levelInfo:
mutex.Lock()
fmt.Print(ProgramName() + ": <INFO> ")
fmt.Println(a...)
mutex.Unlock()
case levelError:
mutex.Lock()
fmt.Fprint(os.Stderr, ProgramName()+": <ERROR> ")
fmt.Fprintln(os.Stderr, a...)
mutex.Unlock()
case levelFatal:
mutex.Lock()
fmt.Fprint(os.Stderr, ProgramName()+": <FATAL> ")
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()+": <DEBUG> ")
fmt.Fprintf(os.Stderr, f, a...)
mutex.Unlock()
case levelInfo:
mutex.Lock()
fmt.Print(ProgramName() + ": <INFO> ")
fmt.Printf(f, a...)
mutex.Unlock()
case levelError:
mutex.Lock()
fmt.Fprint(os.Stderr, ProgramName()+": <ERROR> ")
fmt.Fprintf(os.Stderr, f, a...)
mutex.Unlock()
case levelFatal:
mutex.Lock()
fmt.Fprint(os.Stderr, ProgramName()+": <FATAL> ")
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
}

56
cmd/mkdonut/disks.go Normal file
View File

@ -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
}

118
cmd/mkdonut/main.go Normal file
View File

@ -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()
}

130
cmd/mkdonut/make.go Normal file
View File

@ -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()
}

21
cmd/mkdonut/version.go Normal file
View File

@ -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)
}

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

22
make.go
View File

@ -1,7 +1,7 @@
// +build ignore // +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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,7 +23,6 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"strconv"
"text/template" "text/template"
"time" "time"
@ -49,14 +48,6 @@ import (
// Version autogenerated // Version autogenerated
var Version = {{if .Date}}"{{.Date}}"{{else}}""{{end}} 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 - // getVersion -
func getVersion() string { func getVersion() string {
t, _ := time.Parse(time.RFC3339Nano, Version) t, _ := time.Parse(time.RFC3339Nano, Version)
@ -109,7 +100,7 @@ func runMinioInstall(ctx *cli.Context) {
fmt.Print(minioGenerate) fmt.Print(minioGenerate)
os.Exit(1) os.Exit(1)
} }
fmt.Println(minioGenerate) fmt.Print(minioGenerate)
minioBuildErr := minioBuild.runCommand() minioBuildErr := minioBuild.runCommand()
if minioBuildErr != nil { if minioBuildErr != nil {
fmt.Print(minioBuild) fmt.Print(minioBuild)
@ -136,13 +127,7 @@ func runMinioRelease(ctx *cli.Context) {
} }
t := time.Now().UTC() t := time.Now().UTC()
date := t.Format(time.RFC3339Nano) date := t.Format(time.RFC3339Nano)
// [[STRING]-[EPOCH] version := Version{Date: date}
//
// 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}
err := writeVersion(version) err := writeVersion(version)
if err != nil { if err != nil {
fmt.Print(err) fmt.Print(err)
@ -152,7 +137,6 @@ func runMinioRelease(ctx *cli.Context) {
func main() { func main() {
app := cli.NewApp() app := cli.NewApp()
app.Usage = "Minimalist Object Storage"
app.Commands = []cli.Command{ app.Commands = []cli.Command{
{ {
Name: "release", Name: "release",