From e5773e11c670a70264434c7837f30dd2106ad961 Mon Sep 17 00:00:00 2001 From: Krishnan Parthasarathi Date: Thu, 9 Feb 2017 11:57:35 +0530 Subject: [PATCH] Make minio server compile on OpenBSD, NetBSD, Solaris (#3719) --- buildscripts/checkdeps.sh | 4 +- cmd/globals.go | 2 + cmd/posix-list-dir-nix.go | 2 +- cmd/posix-list-dir-others.go | 2 +- cmd/posix-utils_nix.go | 4 +- cmd/posix.go | 11 +- cmd/server-rlimit-nix.go | 2 +- cmd/server-rlimit-openbsd.go | 83 ++++++ pkg/disk/disk.go | 8 + pkg/disk/disk_test.go | 4 +- pkg/disk/{stat_nix.go => stat_bsd.go} | 9 +- pkg/disk/stat_fallback.go | 24 ++ pkg/disk/stat_linux.go | 39 +++ pkg/disk/stat_openbsd.go | 39 +++ pkg/disk/{type_unix.go => type_bsd.go} | 28 +- pkg/disk/type_darwin.go | 18 +- pkg/disk/type_linux.go | 20 +- pkg/lock/lock_solaris.go | 83 ++++++ vendor/github.com/fatih/color/README.md | 25 +- vendor/github.com/fatih/color/color.go | 244 +++++++++++++----- vendor/github.com/fatih/color/doc.go | 14 + .../mattn/go-isatty/isatty_appengine.go | 9 + .../github.com/mattn/go-isatty/isatty_bsd.go | 2 +- vendor/vendor.json | 10 +- 24 files changed, 545 insertions(+), 141 deletions(-) create mode 100644 cmd/server-rlimit-openbsd.go rename pkg/disk/{stat_nix.go => stat_bsd.go} (85%) create mode 100644 pkg/disk/stat_fallback.go create mode 100644 pkg/disk/stat_linux.go create mode 100644 pkg/disk/stat_openbsd.go rename pkg/disk/{type_unix.go => type_bsd.go} (55%) create mode 100644 pkg/lock/lock_solaris.go create mode 100644 vendor/github.com/mattn/go-isatty/isatty_appengine.go diff --git a/buildscripts/checkdeps.sh b/buildscripts/checkdeps.sh index 057f17b9d..b83fdfa43 100644 --- a/buildscripts/checkdeps.sh +++ b/buildscripts/checkdeps.sh @@ -97,7 +97,7 @@ assert_is_supported_arch() { assert_is_supported_os() { case "${KNAME}" in - Linux | FreeBSD ) + Linux | FreeBSD | OpenBSD | NetBSD | DragonFly ) return ;; Darwin ) @@ -113,7 +113,7 @@ assert_is_supported_os() { *) echo "ERROR" echo "OS '${KNAME}' is not supported." - echo "Supported OS: [Linux, FreeBSD, Darwin]" + echo "Supported OS: [Linux, FreeBSD, OpenBSD, NetBSD, Darwin, DragonFly]" exit 1 esac } diff --git a/cmd/globals.go b/cmd/globals.go index 2fe707ba0..f143d531c 100644 --- a/cmd/globals.go +++ b/cmd/globals.go @@ -51,6 +51,8 @@ const ( globalMinioDefaultOwnerID = "minio" globalMinioDefaultStorageClass = "STANDARD" globalWindowsOSName = "windows" + globalNetBSDOSName = "netbsd" + globalSolarisOSName = "solaris" // Add new global values here. ) diff --git a/cmd/posix-list-dir-nix.go b/cmd/posix-list-dir-nix.go index 1da7eaf76..4e2025b86 100644 --- a/cmd/posix-list-dir-nix.go +++ b/cmd/posix-list-dir-nix.go @@ -1,4 +1,4 @@ -// +build linux darwin dragonfly freebsd netbsd openbsd +// +build linux darwin freebsd netbsd openbsd /* * Minio Cloud Storage, (C) 2016 Minio, Inc. diff --git a/cmd/posix-list-dir-others.go b/cmd/posix-list-dir-others.go index dd40faca5..3cc2ec246 100644 --- a/cmd/posix-list-dir-others.go +++ b/cmd/posix-list-dir-others.go @@ -1,4 +1,4 @@ -// +build !linux,!darwin,!openbsd,!freebsd,!netbsd,!dragonfly +// +build !linux,!darwin,!openbsd,!freebsd,!netbsd /* * Minio Cloud Storage, (C) 2016 Minio, Inc. diff --git a/cmd/posix-utils_nix.go b/cmd/posix-utils_nix.go index ebb59ad61..9b4fa5952 100644 --- a/cmd/posix-utils_nix.go +++ b/cmd/posix-utils_nix.go @@ -1,7 +1,7 @@ -// +build linux darwin dragonfly freebsd netbsd openbsd +// +build linux darwin dragonfly freebsd netbsd openbsd solaris /* - * Minio Cloud Storage, (C) 2016 Minio, Inc. + * Minio Cloud Storage, (C) 2016, 2017 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/cmd/posix.go b/cmd/posix.go index 0a1969fda..89d1c77ed 100644 --- a/cmd/posix.go +++ b/cmd/posix.go @@ -153,11 +153,20 @@ func getDiskInfo(diskPath string) (di disk.Info, err error) { return di, err } +// List of operating systems where we ignore disk space +// verification. +var ignoreDiskFreeOS = []string{ + globalWindowsOSName, + globalNetBSDOSName, + globalSolarisOSName, +} + // checkDiskFree verifies if disk path has sufficient minimum free disk space and files. func (s *posix) checkDiskFree() (err error) { // We don't validate disk space or inode utilization on windows. // Each windows calls to 'GetVolumeInformationW' takes around 3-5seconds. - if runtime.GOOS == globalWindowsOSName { + // And StatFS is not supported by Go for solaris and netbsd. + if contains(ignoreDiskFreeOS, runtime.GOOS) { return nil } diff --git a/cmd/server-rlimit-nix.go b/cmd/server-rlimit-nix.go index 9c0b73f00..03341a6b1 100644 --- a/cmd/server-rlimit-nix.go +++ b/cmd/server-rlimit-nix.go @@ -1,4 +1,4 @@ -// +build !windows,!plan9 +// +build !windows,!plan9,!openbsd /* * Minio Cloud Storage, (C) 2016 Minio, Inc. diff --git a/cmd/server-rlimit-openbsd.go b/cmd/server-rlimit-openbsd.go new file mode 100644 index 000000000..16030447e --- /dev/null +++ b/cmd/server-rlimit-openbsd.go @@ -0,0 +1,83 @@ +// +build openbsd + +/* + * Minio Cloud Storage, (C) 2017 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 ( + "syscall" + + "github.com/minio/minio/pkg/sys" +) + +// For all unixes we need to bump allowed number of open files to a +// higher value than its usual default of '1024'. The reasoning is +// that this value is too small for a server. +func setMaxOpenFiles() error { + var rLimit syscall.Rlimit + err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit) + if err != nil { + return err + } + // Set the current limit to Max, it is usually around 4096. + // TO increase this limit further user has to manually edit + // `/etc/security/limits.conf` + rLimit.Cur = rLimit.Max + return syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) +} + +// Set max memory used by minio as a process, this value is usually +// set to 'unlimited' but we need to validate additionally to verify +// if any hard limit is set by the user, in such a scenario would need +// to reset the global max cache size to be 80% of the hardlimit set +// by the user. This is done to honor the system limits and not crash. +func setMaxMemory() error { + var rLimit syscall.Rlimit + err := syscall.Getrlimit(syscall.RLIMIT_DATA, &rLimit) + if err != nil { + return err + } + // Set the current limit to Max, it is default 'unlimited'. + // TO decrease this limit further user has to manually edit + // `/etc/security/limits.conf` + rLimit.Cur = rLimit.Max + err = syscall.Setrlimit(syscall.RLIMIT_DATA, &rLimit) + if err != nil { + return err + } + err = syscall.Getrlimit(syscall.RLIMIT_DATA, &rLimit) + if err != nil { + return err + } + // Validate if rlimit memory is set to lower + // than max cache size. Then we should use such value. + if uint64(rLimit.Cur) < globalMaxCacheSize { + globalMaxCacheSize = uint64(float64(50*rLimit.Cur) / 100) + } + + // Make sure globalMaxCacheSize is less than RAM size. + stats, err := sys.GetStats() + if err != nil && err != sys.ErrNotImplemented { + return err + } + // If TotalRAM is >= minRAMSize we proceed to enable cache. + // cache is always 50% of the totalRAM. + if err == nil && stats.TotalRAM >= minRAMSize { + globalMaxCacheSize = uint64(float64(50*stats.TotalRAM) / 100) + } + return nil +} diff --git a/pkg/disk/disk.go b/pkg/disk/disk.go index 4eae7c211..8b4265c39 100644 --- a/pkg/disk/disk.go +++ b/pkg/disk/disk.go @@ -27,3 +27,11 @@ type Info struct { Ffree int64 FSType string } + +func b2s(bs []int8) string { + b := make([]byte, len(bs)) + for i, v := range bs { + b[i] = byte(v) + } + return string(b) +} diff --git a/pkg/disk/disk_test.go b/pkg/disk/disk_test.go index 58858a4ff..a1a4915b1 100644 --- a/pkg/disk/disk_test.go +++ b/pkg/disk/disk_test.go @@ -1,5 +1,7 @@ +// +build !netbsd,!solaris + /* - * Minio Cloud Storage, (C) 2015 Minio, Inc. + * Minio Cloud Storage, (C) 2015, 2016, 2017 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/pkg/disk/stat_nix.go b/pkg/disk/stat_bsd.go similarity index 85% rename from pkg/disk/stat_nix.go rename to pkg/disk/stat_bsd.go index 1c1a58a01..31aebd521 100644 --- a/pkg/disk/stat_nix.go +++ b/pkg/disk/stat_bsd.go @@ -1,7 +1,7 @@ -// +build darwin dragonfly freebsd linux netbsd openbsd +// +build darwin freebsd dragonfly /* - * Minio Cloud Storage, (C) 2015 Minio, Inc. + * Minio Cloud Storage, (C) 2015, 2016, 2017 Minio, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,9 +34,6 @@ func GetInfo(path string) (info Info, err error) { info.Free = int64(s.Bsize) * int64(s.Bavail) info.Files = int64(s.Files) info.Ffree = int64(s.Ffree) - info.FSType, err = getFSType(path) - if err != nil { - return Info{}, err - } + info.FSType = getFSType(s.Fstypename) return info, nil } diff --git a/pkg/disk/stat_fallback.go b/pkg/disk/stat_fallback.go new file mode 100644 index 000000000..7439135bb --- /dev/null +++ b/pkg/disk/stat_fallback.go @@ -0,0 +1,24 @@ +// +build netbsd solaris + +/* + * Minio Cloud Storage, (C) 2017 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 disk + +// GetInfo returns total and free bytes available in a directory, e.g. `/`. +func GetInfo(path string) (info Info, err error) { + return Info{}, nil +} diff --git a/pkg/disk/stat_linux.go b/pkg/disk/stat_linux.go new file mode 100644 index 000000000..4106cd985 --- /dev/null +++ b/pkg/disk/stat_linux.go @@ -0,0 +1,39 @@ +// +build linux + +/* + * Minio Cloud Storage, (C) 2015, 2016, 2017 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 disk + +import ( + "syscall" +) + +// GetInfo returns total and free bytes available in a directory, e.g. `/`. +func GetInfo(path string) (info Info, err error) { + s := syscall.Statfs_t{} + err = syscall.Statfs(path, &s) + if err != nil { + return Info{}, err + } + info = Info{} + info.Total = int64(s.Bsize) * int64(s.Blocks) + info.Free = int64(s.Bsize) * int64(s.Bavail) + info.Files = int64(s.Files) + info.Ffree = int64(s.Ffree) + info.FSType = getFSType(int64(s.Type)) + return info, nil +} diff --git a/pkg/disk/stat_openbsd.go b/pkg/disk/stat_openbsd.go new file mode 100644 index 000000000..3a8d43218 --- /dev/null +++ b/pkg/disk/stat_openbsd.go @@ -0,0 +1,39 @@ +// +build openbsd + +/* + * Minio Cloud Storage, (C) 2017 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 disk + +import ( + "syscall" +) + +// GetInfo returns total and free bytes available in a directory, e.g. `/`. +func GetInfo(path string) (info Info, err error) { + s := syscall.Statfs_t{} + err = syscall.Statfs(path, &s) + if err != nil { + return Info{}, err + } + info = Info{} + info.Total = int64(s.F_bsize) * int64(s.F_blocks) + info.Free = int64(s.F_bsize) * int64(s.F_bavail) + info.Files = int64(s.F_files) + info.Ffree = int64(s.F_ffree) + info.FSType = getFSType(s.F_fstypename) + return info, nil +} diff --git a/pkg/disk/type_unix.go b/pkg/disk/type_bsd.go similarity index 55% rename from pkg/disk/type_unix.go rename to pkg/disk/type_bsd.go index 3fe4cafb2..0818f10d6 100644 --- a/pkg/disk/type_unix.go +++ b/pkg/disk/type_bsd.go @@ -1,7 +1,7 @@ -// +build freebsd +// +build darwin freebsd dragonfly openbsd /* - * Minio Cloud Storage, (C) 2015 Minio, Inc. + * Minio Cloud Storage, (C) 2017 Minio, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,27 +18,7 @@ package disk -import ( - "strconv" - "syscall" -) - -// fsType2StringMap - list of filesystems supported by donut on linux -var fsType2StringMap = map[string]string{ - "35": "UFS", -} - // getFSType returns the filesystem type of the underlying mounted filesystem -func getFSType(path string) (string, error) { - s := syscall.Statfs_t{} - err := syscall.Statfs(path, &s) - if err != nil { - return "", err - } - fsTypeHex := strconv.FormatInt(int64(s.Type), 16) - fsTypeString, ok := fsType2StringMap[fsTypeHex] - if ok == false { - return "UNKNOWN", nil - } - return fsTypeString, nil +func getFSType(fstype [16]int8) string { + return b2s(fstype[:]) } diff --git a/pkg/disk/type_darwin.go b/pkg/disk/type_darwin.go index c000e1a08..85cad1aa7 100644 --- a/pkg/disk/type_darwin.go +++ b/pkg/disk/type_darwin.go @@ -18,10 +18,7 @@ package disk -import ( - "strconv" - "syscall" -) +import "strconv" // fsType2StrinMap - list of filesystems supported by donut var fsType2StringMap = map[string]string{ @@ -29,16 +26,11 @@ var fsType2StringMap = map[string]string{ } // getFSType returns the filesystem type of the underlying mounted filesystem -func getFSType(path string) (string, error) { - s := syscall.Statfs_t{} - err := syscall.Statfs(path, &s) - if err != nil { - return "", err - } - fsTypeHex := strconv.FormatUint(uint64(s.Type), 16) +func getFSType(ftype int64) string { + fsTypeHex := strconv.FormatInt(ftype, 16) fsTypeString, ok := fsType2StringMap[fsTypeHex] if !ok { - return "UNKNOWN", nil + return "UNKNOWN" } - return fsTypeString, nil + return fsTypeString } diff --git a/pkg/disk/type_linux.go b/pkg/disk/type_linux.go index 4609c90b0..98dceb66f 100644 --- a/pkg/disk/type_linux.go +++ b/pkg/disk/type_linux.go @@ -1,7 +1,7 @@ // +build linux /* - * Minio Cloud Storage, (C) 2015 Minio, Inc. + * Minio Cloud Storage, (C) 2017 Minio, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,7 @@ package disk -import ( - "strconv" - "syscall" -) +import "strconv" // fsType2StringMap - list of filesystems supported by donut on linux var fsType2StringMap = map[string]string{ @@ -40,16 +37,11 @@ var fsType2StringMap = map[string]string{ } // getFSType returns the filesystem type of the underlying mounted filesystem -func getFSType(path string) (string, error) { - s := syscall.Statfs_t{} - err := syscall.Statfs(path, &s) - if err != nil { - return "", err - } - fsTypeHex := strconv.FormatInt(int64(s.Type), 16) +func getFSType(ftype int64) string { + fsTypeHex := strconv.FormatInt(ftype, 16) fsTypeString, ok := fsType2StringMap[fsTypeHex] if ok == false { - return "UNKNOWN", nil + return "UNKNOWN" } - return fsTypeString, nil + return fsTypeString } diff --git a/pkg/lock/lock_solaris.go b/pkg/lock/lock_solaris.go new file mode 100644 index 000000000..76aa769b6 --- /dev/null +++ b/pkg/lock/lock_solaris.go @@ -0,0 +1,83 @@ +// +build solaris + +/* + * Minio Cloud Storage, (C) 2017 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 lock + +import ( + "fmt" + "os" + "syscall" +) + +// LockedOpenFile - initializes a new lock and protects +// the file from concurrent access across mount points. +// This implementation doesn't support all the open +// flags and shouldn't be considered as replacement +// for os.OpenFile(). +func LockedOpenFile(path string, flag int, perm os.FileMode) (*LockedFile, error) { + var lock syscall.Flock_t + lock.Start = 0 + lock.Len = 0 + lock.Pid = 0 + + var lockType int16 + switch flag { + case syscall.O_RDONLY: + lockType = syscall.F_RDLCK + case syscall.O_WRONLY: + fallthrough + case syscall.O_RDWR: + fallthrough + case syscall.O_WRONLY | syscall.O_CREAT: + fallthrough + case syscall.O_RDWR | syscall.O_CREAT: + lockType = syscall.F_WRLCK + default: + return nil, fmt.Errorf("Unsupported flag (%d)", flag) + } + + lock.Type = lockType + lock.Whence = 0 + + f, err := os.OpenFile(path, flag, perm) + if err != nil { + return nil, err + } + + if err = syscall.FcntlFlock(f.Fd(), syscall.F_SETLKW, &lock); err != nil { + f.Close() + return nil, err + } + + st, err := os.Stat(path) + if err != nil { + f.Close() + return nil, err + } + + if st.IsDir() { + f.Close() + return nil, &os.PathError{ + Op: "open", + Path: path, + Err: syscall.EISDIR, + } + } + + return &LockedFile{f}, nil +} diff --git a/vendor/github.com/fatih/color/README.md b/vendor/github.com/fatih/color/README.md index 0921f98a5..25abbca3f 100644 --- a/vendor/github.com/fatih/color/README.md +++ b/vendor/github.com/fatih/color/README.md @@ -56,6 +56,16 @@ whiteBackground := red.Add(color.BgWhite) whiteBackground.Println("Red text with white background.") ``` +### Use your own output (io.Writer) + +```go +// Use your own io.Writer output +color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + +blue := color.New(color.FgBlue) +blue.Fprint(writer, "This will print text in blue.") +``` + ### Custom print functions (PrintFunc) ```go @@ -69,6 +79,17 @@ notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() notice("Don't forget this...") ``` +### Custom fprint functions (FprintFunc) + +```go +blue := color.New(FgBlue).FprintfFunc() +blue(myWriter, "important notice: %s", stars) + +// Mix up with multiple attributes +success := color.New(color.Bold, color.FgGreen).FprintlnFunc() +success(myWriter, "Don't forget this...") +``` + ### Insert into noncolor strings (SprintFunc) ```go @@ -81,8 +102,8 @@ info := color.New(color.FgWhite, color.BgGreen).SprintFunc() fmt.Printf("This %s rocks!\n", info("package")) // Use helper functions -fmt.Printf("This", color.RedString("warning"), "should be not neglected.") -fmt.Printf(color.GreenString("Info:"), "an important message." ) +fmt.Println("This", color.RedString("warning"), "should be not neglected.") +fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.") // Windows supported too! Just don't forget to change the output to color.Output fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) diff --git a/vendor/github.com/fatih/color/color.go b/vendor/github.com/fatih/color/color.go index e3e997284..dba8f211e 100644 --- a/vendor/github.com/fatih/color/color.go +++ b/vendor/github.com/fatih/color/color.go @@ -2,19 +2,32 @@ package color import ( "fmt" + "io" "os" "strconv" "strings" + "sync" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" ) -// NoColor defines if the output is colorized or not. It's dynamically set to -// false or true based on the stdout's file descriptor referring to a terminal -// or not. This is a global option and affects all colors. For more control -// over each color block use the methods DisableColor() individually. -var NoColor = !isatty.IsTerminal(os.Stdout.Fd()) +var ( + // NoColor defines if the output is colorized or not. It's dynamically set to + // false or true based on the stdout's file descriptor referring to a terminal + // or not. This is a global option and affects all colors. For more control + // over each color block use the methods DisableColor() individually. + NoColor = !isatty.IsTerminal(os.Stdout.Fd()) || os.Getenv("TERM") == "dumb" + + // Output defines the standard output of the print functions. By default + // os.Stdout is used. + Output = colorable.NewColorableStdout() + + // colorsCache is used to reduce the count of created Color objects and + // allows to reuse already created objects with required Attribute. + colorsCache = make(map[Attribute]*Color) + colorsCacheMu sync.Mutex // protects colorsCache +) // Color defines a custom color object which is defined by SGR parameters. type Color struct { @@ -132,6 +145,27 @@ func (c *Color) unset() { Unset() } +func (c *Color) setWriter(w io.Writer) *Color { + if c.isNoColorSet() { + return c + } + + fmt.Fprintf(w, c.format()) + return c +} + +func (c *Color) unsetWriter(w io.Writer) { + if c.isNoColorSet() { + return + } + + if NoColor { + return + } + + fmt.Fprintf(w, "%s[%dm", escape, Reset) +} + // Add is used to chain SGR parameters. Use as many as parameters to combine // and create custom color objects. Example: Add(color.FgRed, color.Underline). func (c *Color) Add(value ...Attribute) *Color { @@ -145,9 +179,17 @@ func (c *Color) prepend(value Attribute) { c.params[0] = value } -// Output defines the standard output of the print functions. By default -// os.Stdout is used. -var Output = colorable.NewColorableStdout() +// Fprint formats using the default formats for its operands and writes to w. +// Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprint(w, a...) +} // Print formats using the default formats for its operands and writes to // standard output. Spaces are added between operands when neither is a @@ -161,6 +203,17 @@ func (c *Color) Print(a ...interface{}) (n int, err error) { return fmt.Fprint(Output, a...) } +// Fprintf formats according to a format specifier and writes to w. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintf(w, format, a...) +} + // Printf formats according to a format specifier and writes to standard output. // It returns the number of bytes written and any write error encountered. // This is the standard fmt.Printf() method wrapped with the given color. @@ -171,6 +224,17 @@ func (c *Color) Printf(format string, a ...interface{}) (n int, err error) { return fmt.Fprintf(Output, format, a...) } +// Fprintln formats using the default formats for its operands and writes to w. +// Spaces are always added between operands and a newline is appended. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintln(w, a...) +} + // Println formats using the default formats for its operands and writes to // standard output. Spaces are always added between operands and a newline is // appended. It returns the number of bytes written and any write error @@ -183,27 +247,57 @@ func (c *Color) Println(a ...interface{}) (n int, err error) { return fmt.Fprintln(Output, a...) } +// FprintFunc returns a new function that prints the passed arguments as +// colorized with color.Fprint(). +func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprint(w, a...) + } +} + // PrintFunc returns a new function that prints the passed arguments as // colorized with color.Print(). func (c *Color) PrintFunc() func(a ...interface{}) { - return func(a ...interface{}) { c.Print(a...) } + return func(a ...interface{}) { + c.Print(a...) + } +} + +// FprintfFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintf(). +func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) { + return func(w io.Writer, format string, a ...interface{}) { + c.Fprintf(w, format, a...) + } } // PrintfFunc returns a new function that prints the passed arguments as // colorized with color.Printf(). func (c *Color) PrintfFunc() func(format string, a ...interface{}) { - return func(format string, a ...interface{}) { c.Printf(format, a...) } + return func(format string, a ...interface{}) { + c.Printf(format, a...) + } +} + +// FprintlnFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintln(). +func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprintln(w, a...) + } } // PrintlnFunc returns a new function that prints the passed arguments as // colorized with color.Println(). func (c *Color) PrintlnFunc() func(a ...interface{}) { - return func(a ...interface{}) { c.Println(a...) } + return func(a ...interface{}) { + c.Println(a...) + } } // SprintFunc returns a new function that returns colorized strings for the // given arguments with fmt.Sprint(). Useful to put into or mix into other -// string. Windows users should use this in conjuction with color.Output, example: +// string. Windows users should use this in conjunction with color.Output, example: // // put := New(FgYellow).SprintFunc() // fmt.Fprintf(color.Output, "This is a %s", put("warning")) @@ -215,7 +309,7 @@ func (c *Color) SprintFunc() func(a ...interface{}) string { // SprintfFunc returns a new function that returns colorized strings for the // given arguments with fmt.Sprintf(). Useful to put into or mix into other -// string. Windows users should use this in conjuction with color.Output. +// string. Windows users should use this in conjunction with color.Output. func (c *Color) SprintfFunc() func(format string, a ...interface{}) string { return func(format string, a ...interface{}) string { return c.wrap(fmt.Sprintf(format, a...)) @@ -224,7 +318,7 @@ func (c *Color) SprintfFunc() func(format string, a ...interface{}) string { // SprintlnFunc returns a new function that returns colorized strings for the // given arguments with fmt.Sprintln(). Useful to put into or mix into other -// string. Windows users should use this in conjuction with color.Output. +// string. Windows users should use this in conjunction with color.Output. func (c *Color) SprintlnFunc() func(a ...interface{}) string { return func(a ...interface{}) string { return c.wrap(fmt.Sprintln(a...)) @@ -267,7 +361,7 @@ func (c *Color) DisableColor() { c.noColor = boolPtr(true) } -// EnableColor enables the color output. Use it in conjuction with +// EnableColor enables the color output. Use it in conjunction with // DisableColor(). Otherwise this method has no side effects. func (c *Color) EnableColor() { c.noColor = boolPtr(false) @@ -312,91 +406,105 @@ func boolPtr(v bool) *bool { return &v } -// Black is an convenient helper function to print with black foreground. A -// newline is appended to format by default. -func Black(format string, a ...interface{}) { printColor(format, FgBlack, a...) } +func getCachedColor(p Attribute) *Color { + colorsCacheMu.Lock() + defer colorsCacheMu.Unlock() -// Red is an convenient helper function to print with red foreground. A -// newline is appended to format by default. -func Red(format string, a ...interface{}) { printColor(format, FgRed, a...) } + c, ok := colorsCache[p] + if !ok { + c = New(p) + colorsCache[p] = c + } -// Green is an convenient helper function to print with green foreground. A -// newline is appended to format by default. -func Green(format string, a ...interface{}) { printColor(format, FgGreen, a...) } + return c +} -// Yellow is an convenient helper function to print with yellow foreground. -// A newline is appended to format by default. -func Yellow(format string, a ...interface{}) { printColor(format, FgYellow, a...) } +func colorPrint(format string, p Attribute, a ...interface{}) { + c := getCachedColor(p) -// Blue is an convenient helper function to print with blue foreground. A -// newline is appended to format by default. -func Blue(format string, a ...interface{}) { printColor(format, FgBlue, a...) } - -// Magenta is an convenient helper function to print with magenta foreground. -// A newline is appended to format by default. -func Magenta(format string, a ...interface{}) { printColor(format, FgMagenta, a...) } - -// Cyan is an convenient helper function to print with cyan foreground. A -// newline is appended to format by default. -func Cyan(format string, a ...interface{}) { printColor(format, FgCyan, a...) } - -// White is an convenient helper function to print with white foreground. A -// newline is appended to format by default. -func White(format string, a ...interface{}) { printColor(format, FgWhite, a...) } - -func printColor(format string, p Attribute, a ...interface{}) { if !strings.HasSuffix(format, "\n") { format += "\n" } - c := &Color{params: []Attribute{p}} - c.Printf(format, a...) + if len(a) == 0 { + c.Print(format) + } else { + c.Printf(format, a...) + } } +func colorString(format string, p Attribute, a ...interface{}) string { + c := getCachedColor(p) + + if len(a) == 0 { + return c.SprintFunc()(format) + } + + return c.SprintfFunc()(format, a...) +} + +// Black is an convenient helper function to print with black foreground. A +// newline is appended to format by default. +func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) } + +// Red is an convenient helper function to print with red foreground. A +// newline is appended to format by default. +func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) } + +// Green is an convenient helper function to print with green foreground. A +// newline is appended to format by default. +func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) } + +// Yellow is an convenient helper function to print with yellow foreground. +// A newline is appended to format by default. +func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) } + +// Blue is an convenient helper function to print with blue foreground. A +// newline is appended to format by default. +func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) } + +// Magenta is an convenient helper function to print with magenta foreground. +// A newline is appended to format by default. +func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) } + +// Cyan is an convenient helper function to print with cyan foreground. A +// newline is appended to format by default. +func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) } + +// White is an convenient helper function to print with white foreground. A +// newline is appended to format by default. +func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) } + // BlackString is an convenient helper function to return a string with black // foreground. -func BlackString(format string, a ...interface{}) string { - return New(FgBlack).SprintfFunc()(format, a...) -} +func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) } // RedString is an convenient helper function to return a string with red // foreground. -func RedString(format string, a ...interface{}) string { - return New(FgRed).SprintfFunc()(format, a...) -} +func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) } // GreenString is an convenient helper function to return a string with green // foreground. -func GreenString(format string, a ...interface{}) string { - return New(FgGreen).SprintfFunc()(format, a...) -} +func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) } // YellowString is an convenient helper function to return a string with yellow // foreground. -func YellowString(format string, a ...interface{}) string { - return New(FgYellow).SprintfFunc()(format, a...) -} +func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) } // BlueString is an convenient helper function to return a string with blue // foreground. -func BlueString(format string, a ...interface{}) string { - return New(FgBlue).SprintfFunc()(format, a...) -} +func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) } // MagentaString is an convenient helper function to return a string with magenta // foreground. func MagentaString(format string, a ...interface{}) string { - return New(FgMagenta).SprintfFunc()(format, a...) + return colorString(format, FgMagenta, a...) } // CyanString is an convenient helper function to return a string with cyan // foreground. -func CyanString(format string, a ...interface{}) string { - return New(FgCyan).SprintfFunc()(format, a...) -} +func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) } // WhiteString is an convenient helper function to return a string with white // foreground. -func WhiteString(format string, a ...interface{}) string { - return New(FgWhite).SprintfFunc()(format, a...) -} +func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) } diff --git a/vendor/github.com/fatih/color/doc.go b/vendor/github.com/fatih/color/doc.go index 17908787c..1e57812d7 100644 --- a/vendor/github.com/fatih/color/doc.go +++ b/vendor/github.com/fatih/color/doc.go @@ -37,6 +37,11 @@ separate color object. whiteBackground := red.Add(color.BgWhite) whiteBackground.Println("Red text with White background.") + // Use your own io.Writer output + color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + + blue := color.New(color.FgBlue) + blue.Fprint(myWriter, "This will print text in blue.") You can create PrintXxx functions to simplify even more: @@ -49,6 +54,15 @@ You can create PrintXxx functions to simplify even more: notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() notice("don't forget this...") +You can also FprintXxx functions to pass your own io.Writer: + + blue := color.New(FgBlue).FprintfFunc() + blue(myWriter, "important notice: %s", stars) + + // Mix up with multiple attributes + success := color.New(color.Bold, color.FgGreen).FprintlnFunc() + success(myWriter, don't forget this...") + Or create SprintXxx functions to mix strings with other non-colorized strings: diff --git a/vendor/github.com/mattn/go-isatty/isatty_appengine.go b/vendor/github.com/mattn/go-isatty/isatty_appengine.go new file mode 100644 index 000000000..83c588773 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_appengine.go @@ -0,0 +1,9 @@ +// +build appengine + +package isatty + +// IsTerminal returns true if the file descriptor is terminal which +// is always false on on appengine classic which is a sandboxed PaaS. +func IsTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go index 98ffe86a4..42f2514d1 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_bsd.go +++ b/vendor/github.com/mattn/go-isatty/isatty_bsd.go @@ -1,4 +1,4 @@ -// +build darwin freebsd openbsd netbsd +// +build darwin freebsd openbsd netbsd dragonfly // +build !appengine package isatty diff --git a/vendor/vendor.json b/vendor/vendor.json index 87783717c..ef5b851e1 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -57,9 +57,10 @@ "revisionTime": "2015-12-24T06:54:52+02:00" }, { + "checksumSHA1": "XEBl9iQQvfXNlwCSzyAOCqAlYEA=", "path": "github.com/fatih/color", - "revision": "87d4004f2ab62d0d255e0a38f1680aa534549fe3", - "revisionTime": "2016-06-10T14:06:02+03:00" + "revision": "42c364ba490082e4815b5222728711b3440603eb", + "revisionTime": "2017-01-13T15:16:12Z" }, { "checksumSHA1": "3yco0089CSJ4qbyUccpbDC2+dPg=", @@ -150,9 +151,10 @@ "revisionTime": "2016-06-03T00:08:27+09:00" }, { + "checksumSHA1": "xZuhljnmBysJPta/lMyYmJdujCg=", "path": "github.com/mattn/go-isatty", - "revision": "56b76bdf51f7708750eac80fa38b952bb9f32639", - "revisionTime": "2015-12-11T09:06:21+09:00" + "revision": "30a891c33c7cde7b02a981314b4228ec99380cca", + "revisionTime": "2016-11-23T14:36:37Z" }, { "checksumSHA1": "4gphCNVXIjp5ytz3+S3SD3Dp948=",