mirror of https://github.com/k3s-io/k3s
commit
09a06448d3
|
@ -151,33 +151,33 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/docker/pkg/jsonmessage",
|
||||
"Comment": "v1.7.rc",
|
||||
"Rev": "0cd6c05d8112e9246b734107d54e2855e3d5fec5"
|
||||
"Comment": "v1.4.1-4045-g2b27fe1",
|
||||
"Rev": "2b27fe17a1b3fb8472fde96d768fa70996adf201"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/docker/pkg/mount",
|
||||
"Comment": "v1.4.1-1714-ged66853",
|
||||
"Rev": "ed6685369740035b0af9675bf9add52d0af7657b"
|
||||
"Comment": "v1.4.1-4045-g2b27fe1",
|
||||
"Rev": "2b27fe17a1b3fb8472fde96d768fa70996adf201"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/docker/pkg/parsers",
|
||||
"Comment": "v1.4.1-1714-ged66853",
|
||||
"Rev": "ed6685369740035b0af9675bf9add52d0af7657b"
|
||||
"Comment": "v1.4.1-4045-g2b27fe1",
|
||||
"Rev": "2b27fe17a1b3fb8472fde96d768fa70996adf201"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/docker/pkg/term",
|
||||
"Comment": "v1.4.1-1714-ged66853",
|
||||
"Rev": "ed6685369740035b0af9675bf9add52d0af7657b"
|
||||
"Comment": "v1.4.1-4045-g2b27fe1",
|
||||
"Rev": "2b27fe17a1b3fb8472fde96d768fa70996adf201"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/docker/pkg/timeutils",
|
||||
"Comment": "v1.7.rc",
|
||||
"Rev": "0cd6c05d8112e9246b734107d54e2855e3d5fec5"
|
||||
"Comment": "v1.4.1-4045-g2b27fe1",
|
||||
"Rev": "2b27fe17a1b3fb8472fde96d768fa70996adf201"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/docker/pkg/units",
|
||||
"Comment": "v1.4.1-1714-ged66853",
|
||||
"Rev": "ed6685369740035b0af9675bf9add52d0af7657b"
|
||||
"Comment": "v1.4.1-4045-g2b27fe1",
|
||||
"Rev": "2b27fe17a1b3fb8472fde96d768fa70996adf201"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/libcontainer",
|
||||
|
|
186
Godeps/_workspace/src/github.com/docker/docker/pkg/jsonmessage/jsonmessage_test.go
generated
vendored
186
Godeps/_workspace/src/github.com/docker/docker/pkg/jsonmessage/jsonmessage_test.go
generated
vendored
|
@ -1,7 +1,14 @@
|
|||
package jsonmessage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/docker/pkg/timeutils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
|
@ -23,16 +30,181 @@ func TestProgress(t *testing.T) {
|
|||
t.Fatalf("Expected %q, got %q", expected, jp2.String())
|
||||
}
|
||||
|
||||
expected = "[=========================> ] 50 B/100 B"
|
||||
jp3 := JSONProgress{Current: 50, Total: 100}
|
||||
if jp3.String() != expected {
|
||||
t.Fatalf("Expected %q, got %q", expected, jp3.String())
|
||||
expectedStart := "[==========> ] 20 B/100 B"
|
||||
jp3 := JSONProgress{Current: 20, Total: 100, Start: time.Now().Unix()}
|
||||
// Just look at the start of the string
|
||||
// (the remaining time is really hard to test -_-)
|
||||
if jp3.String()[:len(expectedStart)] != expectedStart {
|
||||
t.Fatalf("Expected to start with %q, got %q", expectedStart, jp3.String())
|
||||
}
|
||||
|
||||
// this number can't be negetive gh#7136
|
||||
expected = "[==================================================>] 50 B/40 B"
|
||||
jp4 := JSONProgress{Current: 50, Total: 40}
|
||||
expected = "[=========================> ] 50 B/100 B"
|
||||
jp4 := JSONProgress{Current: 50, Total: 100}
|
||||
if jp4.String() != expected {
|
||||
t.Fatalf("Expected %q, got %q", expected, jp4.String())
|
||||
}
|
||||
|
||||
// this number can't be negative gh#7136
|
||||
expected = "[==================================================>] 50 B/40 B"
|
||||
jp5 := JSONProgress{Current: 50, Total: 40}
|
||||
if jp5.String() != expected {
|
||||
t.Fatalf("Expected %q, got %q", expected, jp5.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONMessageDisplay(t *testing.T) {
|
||||
now := time.Now().Unix()
|
||||
messages := map[JSONMessage][]string{
|
||||
// Empty
|
||||
JSONMessage{}: {"\n", "\n"},
|
||||
// Status
|
||||
JSONMessage{
|
||||
Status: "status",
|
||||
}: {
|
||||
"status\n",
|
||||
"status\n",
|
||||
},
|
||||
// General
|
||||
JSONMessage{
|
||||
Time: now,
|
||||
ID: "ID",
|
||||
From: "From",
|
||||
Status: "status",
|
||||
}: {
|
||||
fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now, 0).Format(timeutils.RFC3339NanoFixed)),
|
||||
fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now, 0).Format(timeutils.RFC3339NanoFixed)),
|
||||
},
|
||||
// Stream over status
|
||||
JSONMessage{
|
||||
Status: "status",
|
||||
Stream: "stream",
|
||||
}: {
|
||||
"stream",
|
||||
"stream",
|
||||
},
|
||||
// With progress message
|
||||
JSONMessage{
|
||||
Status: "status",
|
||||
ProgressMessage: "progressMessage",
|
||||
}: {
|
||||
"status progressMessage",
|
||||
"status progressMessage",
|
||||
},
|
||||
// With progress, stream empty
|
||||
JSONMessage{
|
||||
Status: "status",
|
||||
Stream: "",
|
||||
Progress: &JSONProgress{Current: 1},
|
||||
}: {
|
||||
"",
|
||||
fmt.Sprintf("%c[2K\rstatus 1 B\r", 27),
|
||||
},
|
||||
}
|
||||
|
||||
// The tests :)
|
||||
for jsonMessage, expectedMessages := range messages {
|
||||
// Without terminal
|
||||
data := bytes.NewBuffer([]byte{})
|
||||
if err := jsonMessage.Display(data, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if data.String() != expectedMessages[0] {
|
||||
t.Fatalf("Expected [%v], got [%v]", expectedMessages[0], data.String())
|
||||
}
|
||||
// With terminal
|
||||
data = bytes.NewBuffer([]byte{})
|
||||
if err := jsonMessage.Display(data, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if data.String() != expectedMessages[1] {
|
||||
t.Fatalf("Expected [%v], got [%v]", expectedMessages[1], data.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test JSONMessage with an Error. It will return an error with the text as error, not the meaning of the HTTP code.
|
||||
func TestJSONMessageDisplayWithJSONError(t *testing.T) {
|
||||
data := bytes.NewBuffer([]byte{})
|
||||
jsonMessage := JSONMessage{Error: &JSONError{404, "Can't find it"}}
|
||||
|
||||
err := jsonMessage.Display(data, true)
|
||||
if err == nil || err.Error() != "Can't find it" {
|
||||
t.Fatalf("Expected a JSONError 404, got [%v]", err)
|
||||
}
|
||||
|
||||
jsonMessage = JSONMessage{Error: &JSONError{401, "Anything"}}
|
||||
err = jsonMessage.Display(data, true)
|
||||
if err == nil || err.Error() != "Authentication is required." {
|
||||
t.Fatalf("Expected an error [Authentication is required.], got [%v]", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisplayJSONMessagesStreamInvalidJSON(t *testing.T) {
|
||||
var (
|
||||
inFd uintptr
|
||||
)
|
||||
data := bytes.NewBuffer([]byte{})
|
||||
reader := strings.NewReader("This is not a 'valid' JSON []")
|
||||
inFd, _ = term.GetFdInfo(reader)
|
||||
|
||||
if err := DisplayJSONMessagesStream(reader, data, inFd, false); err == nil && err.Error()[:17] != "invalid character" {
|
||||
t.Fatalf("Should have thrown an error (invalid character in ..), got [%v]", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisplayJSONMessagesStream(t *testing.T) {
|
||||
var (
|
||||
inFd uintptr
|
||||
)
|
||||
|
||||
messages := map[string][]string{
|
||||
// empty string
|
||||
"": {
|
||||
"",
|
||||
""},
|
||||
// Without progress & ID
|
||||
"{ \"status\": \"status\" }": {
|
||||
"status\n",
|
||||
"status\n",
|
||||
},
|
||||
// Without progress, with ID
|
||||
"{ \"id\": \"ID\",\"status\": \"status\" }": {
|
||||
"ID: status\n",
|
||||
fmt.Sprintf("ID: status\n%c[%dB", 27, 0),
|
||||
},
|
||||
// With progress
|
||||
"{ \"id\": \"ID\", \"status\": \"status\", \"progress\": \"ProgressMessage\" }": {
|
||||
"ID: status ProgressMessage",
|
||||
fmt.Sprintf("\n%c[%dAID: status ProgressMessage%c[%dB", 27, 0, 27, 0),
|
||||
},
|
||||
// With progressDetail
|
||||
"{ \"id\": \"ID\", \"status\": \"status\", \"progressDetail\": { \"Current\": 1} }": {
|
||||
"", // progressbar is disabled in non-terminal
|
||||
fmt.Sprintf("\n%c[%dA%c[2K\rID: status 1 B\r%c[%dB", 27, 0, 27, 27, 0),
|
||||
},
|
||||
}
|
||||
for jsonMessage, expectedMessages := range messages {
|
||||
data := bytes.NewBuffer([]byte{})
|
||||
reader := strings.NewReader(jsonMessage)
|
||||
inFd, _ = term.GetFdInfo(reader)
|
||||
|
||||
// Without terminal
|
||||
if err := DisplayJSONMessagesStream(reader, data, inFd, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if data.String() != expectedMessages[0] {
|
||||
t.Fatalf("Expected an [%v], got [%v]", expectedMessages[0], data.String())
|
||||
}
|
||||
|
||||
// With terminal
|
||||
data = bytes.NewBuffer([]byte{})
|
||||
reader = strings.NewReader(jsonMessage)
|
||||
if err := DisplayJSONMessagesStream(reader, data, inFd, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if data.String() != expectedMessages[1] {
|
||||
t.Fatalf("Expected an [%v], got [%v]", expectedMessages[1], data.String())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,12 +8,25 @@ package mount
|
|||
import "C"
|
||||
|
||||
const (
|
||||
RDONLY = C.MNT_RDONLY
|
||||
NOSUID = C.MNT_NOSUID
|
||||
NOEXEC = C.MNT_NOEXEC
|
||||
SYNCHRONOUS = C.MNT_SYNCHRONOUS
|
||||
NOATIME = C.MNT_NOATIME
|
||||
// RDONLY will mount the filesystem as read-only.
|
||||
RDONLY = C.MNT_RDONLY
|
||||
|
||||
// NOSUID will not allow set-user-identifier or set-group-identifier bits to
|
||||
// take effect.
|
||||
NOSUID = C.MNT_NOSUID
|
||||
|
||||
// NOEXEC will not allow execution of any binaries on the mounted file system.
|
||||
NOEXEC = C.MNT_NOEXEC
|
||||
|
||||
// SYNCHRONOUS will allow any I/O to the file system to be done synchronously.
|
||||
SYNCHRONOUS = C.MNT_SYNCHRONOUS
|
||||
|
||||
// NOATIME will not update the file access time when reading from a file.
|
||||
NOATIME = C.MNT_NOATIME
|
||||
)
|
||||
|
||||
// These flags are unsupported.
|
||||
const (
|
||||
BIND = 0
|
||||
DIRSYNC = 0
|
||||
MANDLOCK = 0
|
||||
|
|
|
@ -5,26 +5,81 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
RDONLY = syscall.MS_RDONLY
|
||||
NOSUID = syscall.MS_NOSUID
|
||||
NODEV = syscall.MS_NODEV
|
||||
NOEXEC = syscall.MS_NOEXEC
|
||||
// RDONLY will mount the file system read-only.
|
||||
RDONLY = syscall.MS_RDONLY
|
||||
|
||||
// NOSUID will not allow set-user-identifier or set-group-identifier bits to
|
||||
// take effect.
|
||||
NOSUID = syscall.MS_NOSUID
|
||||
|
||||
// NODEV will not interpret character or block special devices on the file
|
||||
// system.
|
||||
NODEV = syscall.MS_NODEV
|
||||
|
||||
// NOEXEC will not allow execution of any binaries on the mounted file system.
|
||||
NOEXEC = syscall.MS_NOEXEC
|
||||
|
||||
// SYNCHRONOUS will allow I/O to the file system to be done synchronously.
|
||||
SYNCHRONOUS = syscall.MS_SYNCHRONOUS
|
||||
DIRSYNC = syscall.MS_DIRSYNC
|
||||
REMOUNT = syscall.MS_REMOUNT
|
||||
MANDLOCK = syscall.MS_MANDLOCK
|
||||
NOATIME = syscall.MS_NOATIME
|
||||
NODIRATIME = syscall.MS_NODIRATIME
|
||||
BIND = syscall.MS_BIND
|
||||
RBIND = syscall.MS_BIND | syscall.MS_REC
|
||||
UNBINDABLE = syscall.MS_UNBINDABLE
|
||||
|
||||
// DIRSYNC will force all directory updates within the file system to be done
|
||||
// synchronously. This affects the following system calls: creat, link,
|
||||
// unlink, symlink, mkdir, rmdir, mknod and rename.
|
||||
DIRSYNC = syscall.MS_DIRSYNC
|
||||
|
||||
// REMOUNT will attempt to remount an already-mounted file system. This is
|
||||
// commonly used to change the mount flags for a file system, especially to
|
||||
// make a readonly file system writeable. It does not change device or mount
|
||||
// point.
|
||||
REMOUNT = syscall.MS_REMOUNT
|
||||
|
||||
// MANDLOCK will force mandatory locks on a filesystem.
|
||||
MANDLOCK = syscall.MS_MANDLOCK
|
||||
|
||||
// NOATIME will not update the file access time when reading from a file.
|
||||
NOATIME = syscall.MS_NOATIME
|
||||
|
||||
// NODIRATIME will not update the directory access time.
|
||||
NODIRATIME = syscall.MS_NODIRATIME
|
||||
|
||||
// BIND remounts a subtree somewhere else.
|
||||
BIND = syscall.MS_BIND
|
||||
|
||||
// RBIND remounts a subtree and all possible submounts somewhere else.
|
||||
RBIND = syscall.MS_BIND | syscall.MS_REC
|
||||
|
||||
// UNBINDABLE creates a mount which cannot be cloned through a bind operation.
|
||||
UNBINDABLE = syscall.MS_UNBINDABLE
|
||||
|
||||
// RUNBINDABLE marks the entire mount tree as UNBINDABLE.
|
||||
RUNBINDABLE = syscall.MS_UNBINDABLE | syscall.MS_REC
|
||||
PRIVATE = syscall.MS_PRIVATE
|
||||
RPRIVATE = syscall.MS_PRIVATE | syscall.MS_REC
|
||||
SLAVE = syscall.MS_SLAVE
|
||||
RSLAVE = syscall.MS_SLAVE | syscall.MS_REC
|
||||
SHARED = syscall.MS_SHARED
|
||||
RSHARED = syscall.MS_SHARED | syscall.MS_REC
|
||||
RELATIME = syscall.MS_RELATIME
|
||||
|
||||
// PRIVATE creates a mount which carries no propagation abilities.
|
||||
PRIVATE = syscall.MS_PRIVATE
|
||||
|
||||
// RPRIVATE marks the entire mount tree as PRIVATE.
|
||||
RPRIVATE = syscall.MS_PRIVATE | syscall.MS_REC
|
||||
|
||||
// SLAVE creates a mount which receives propagation from its master, but not
|
||||
// vice versa.
|
||||
SLAVE = syscall.MS_SLAVE
|
||||
|
||||
// RSLAVE marks the entire mount tree as SLAVE.
|
||||
RSLAVE = syscall.MS_SLAVE | syscall.MS_REC
|
||||
|
||||
// SHARED creates a mount which provides the ability to create mirrors of
|
||||
// that mount such that mounts and unmounts within any of the mirrors
|
||||
// propagate to the other mirrors.
|
||||
SHARED = syscall.MS_SHARED
|
||||
|
||||
// RSHARED marks the entire mount tree as SHARED.
|
||||
RSHARED = syscall.MS_SHARED | syscall.MS_REC
|
||||
|
||||
// RELATIME updates inode access times relative to modify or change time.
|
||||
RELATIME = syscall.MS_RELATIME
|
||||
|
||||
// STRICTATIME allows to explicitly request full atime updates. This makes
|
||||
// it possible for the kernel to default to relatime or noatime but still
|
||||
// allow userspace to override it.
|
||||
STRICTATIME = syscall.MS_STRICTATIME
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
package mount
|
||||
|
||||
// These flags are unsupported.
|
||||
const (
|
||||
BIND = 0
|
||||
DIRSYNC = 0
|
||||
|
|
|
@ -4,11 +4,12 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// GetMounts retrieves a list of mounts for the current running process.
|
||||
func GetMounts() ([]*MountInfo, error) {
|
||||
return parseMountTable()
|
||||
}
|
||||
|
||||
// Looks at /proc/self/mountinfo to determine of the specified
|
||||
// Mounted looks at /proc/self/mountinfo to determine of the specified
|
||||
// mountpoint has been mounted
|
||||
func Mounted(mountpoint string) (bool, error) {
|
||||
entries, err := parseMountTable()
|
||||
|
@ -25,9 +26,10 @@ func Mounted(mountpoint string) (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
// Mount the specified options at the target path only if
|
||||
// the target is not mounted
|
||||
// Options must be specified as fstab style
|
||||
// Mount will mount filesystem according to the specified configuration, on the
|
||||
// condition that the target path is *not* already mounted. Options must be
|
||||
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
|
||||
// flags.go for supported option flags.
|
||||
func Mount(device, target, mType, options string) error {
|
||||
flag, _ := parseOptions(options)
|
||||
if flag&REMOUNT != REMOUNT {
|
||||
|
@ -38,9 +40,10 @@ func Mount(device, target, mType, options string) error {
|
|||
return ForceMount(device, target, mType, options)
|
||||
}
|
||||
|
||||
// Mount the specified options at the target path
|
||||
// reguardless if the target is mounted or not
|
||||
// Options must be specified as fstab style
|
||||
// ForceMount will mount a filesystem according to the specified configuration,
|
||||
// *regardless* if the target path is not already mounted. Options must be
|
||||
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
|
||||
// flags.go for supported option flags.
|
||||
func ForceMount(device, target, mType, options string) error {
|
||||
flag, data := parseOptions(options)
|
||||
if err := mount(device, target, mType, uintptr(flag), data); err != nil {
|
||||
|
@ -49,7 +52,7 @@ func ForceMount(device, target, mType, options string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Unmount the target only if it is mounted
|
||||
// Unmount will unmount the target filesystem, so long as it is mounted.
|
||||
func Unmount(target string) error {
|
||||
if mounted, err := Mounted(target); err != nil || !mounted {
|
||||
return err
|
||||
|
@ -57,7 +60,8 @@ func Unmount(target string) error {
|
|||
return ForceUnmount(target)
|
||||
}
|
||||
|
||||
// Unmount the target reguardless if it is mounted or not
|
||||
// ForceUnmount will force an unmount of the target filesystem, regardless if
|
||||
// it is mounted or not.
|
||||
func ForceUnmount(target string) (err error) {
|
||||
// Simple retry logic for unmount
|
||||
for i := 0; i < 10; i++ {
|
||||
|
|
|
@ -1,7 +1,40 @@
|
|||
package mount
|
||||
|
||||
// MountInfo reveals information about a particular mounted filesystem. This
|
||||
// struct is populated from the content in the /proc/<pid>/mountinfo file.
|
||||
type MountInfo struct {
|
||||
Id, Parent, Major, Minor int
|
||||
Root, Mountpoint, Opts, Optional string
|
||||
Fstype, Source, VfsOpts string
|
||||
// Id is a unique identifier of the mount (may be reused after umount).
|
||||
Id int
|
||||
|
||||
// Parent indicates the ID of the mount parent (or of self for the top of the
|
||||
// mount tree).
|
||||
Parent int
|
||||
|
||||
// Major indicates one half of the device ID which identifies the device class.
|
||||
Major int
|
||||
|
||||
// Minor indicates one half of the device ID which identifies a specific
|
||||
// instance of device.
|
||||
Minor int
|
||||
|
||||
// Root of the mount within the filesystem.
|
||||
Root string
|
||||
|
||||
// Mountpoint indicates the mount point relative to the process's root.
|
||||
Mountpoint string
|
||||
|
||||
// Opts represents mount-specific options.
|
||||
Opts string
|
||||
|
||||
// Optional represents optional fields.
|
||||
Optional string
|
||||
|
||||
// Fstype indicates the type of filesystem, such as EXT3.
|
||||
Fstype string
|
||||
|
||||
// Source indicates filesystem specific information or "none".
|
||||
Source string
|
||||
|
||||
// VfsOpts represents per super block options.
|
||||
VfsOpts string
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ import (
|
|||
"unsafe"
|
||||
)
|
||||
|
||||
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from bind mounts
|
||||
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
|
||||
// bind mounts.
|
||||
func parseMountTable() ([]*MountInfo, error) {
|
||||
var rawEntries *C.struct_statfs
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ const (
|
|||
mountinfoFormat = "%d %d %d:%d %s %s %s %s"
|
||||
)
|
||||
|
||||
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from bind mounts
|
||||
// Parse /proc/self/mountinfo because comparing Dev and ino does not work from
|
||||
// bind mounts
|
||||
func parseMountTable() ([]*MountInfo, error) {
|
||||
f, err := os.Open("/proc/self/mountinfo")
|
||||
if err != nil {
|
||||
|
@ -80,7 +81,9 @@ func parseInfoFile(r io.Reader) ([]*MountInfo, error) {
|
|||
return out, nil
|
||||
}
|
||||
|
||||
// PidMountInfo collects the mounts for a specific Pid
|
||||
// PidMountInfo collects the mounts for a specific process ID. If the process
|
||||
// ID is unknown, it is better to use `GetMounts` which will inspect
|
||||
// "/proc/self/mountinfo" instead.
|
||||
func PidMountInfo(pid int) ([]*MountInfo, error) {
|
||||
f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
|
||||
if err != nil {
|
||||
|
|
16
Godeps/_workspace/src/github.com/docker/docker/pkg/mount/sharedsubtree_linux.go
generated
vendored
16
Godeps/_workspace/src/github.com/docker/docker/pkg/mount/sharedsubtree_linux.go
generated
vendored
|
@ -2,34 +2,50 @@
|
|||
|
||||
package mount
|
||||
|
||||
// MakeShared ensures a mounted filesystem has the SHARED mount option enabled.
|
||||
// See the supported options in flags.go for further reference.
|
||||
func MakeShared(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "shared")
|
||||
}
|
||||
|
||||
// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled.
|
||||
// See the supported options in flags.go for further reference.
|
||||
func MakeRShared(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "rshared")
|
||||
}
|
||||
|
||||
// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled.
|
||||
// See the supported options in flags.go for further reference.
|
||||
func MakePrivate(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "private")
|
||||
}
|
||||
|
||||
// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option
|
||||
// enabled. See the supported options in flags.go for further reference.
|
||||
func MakeRPrivate(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "rprivate")
|
||||
}
|
||||
|
||||
// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled.
|
||||
// See the supported options in flags.go for further reference.
|
||||
func MakeSlave(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "slave")
|
||||
}
|
||||
|
||||
// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled.
|
||||
// See the supported options in flags.go for further reference.
|
||||
func MakeRSlave(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "rslave")
|
||||
}
|
||||
|
||||
// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option
|
||||
// enabled. See the supported options in flags.go for further reference.
|
||||
func MakeUnbindable(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "unbindable")
|
||||
}
|
||||
|
||||
// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount
|
||||
// option enabled. See the supported options in flags.go for further reference.
|
||||
func MakeRUnbindable(mountPoint string) error {
|
||||
return ensureMountedAs(mountPoint, "runbindable")
|
||||
}
|
||||
|
|
116
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse.go
generated
vendored
Normal file
116
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse.go
generated
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
package filters
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Args map[string][]string
|
||||
|
||||
// Parse the argument to the filter flag. Like
|
||||
//
|
||||
// `docker ps -f 'created=today' -f 'image.name=ubuntu*'`
|
||||
//
|
||||
// If prev map is provided, then it is appended to, and returned. By default a new
|
||||
// map is created.
|
||||
func ParseFlag(arg string, prev Args) (Args, error) {
|
||||
var filters Args = prev
|
||||
if prev == nil {
|
||||
filters = Args{}
|
||||
}
|
||||
if len(arg) == 0 {
|
||||
return filters, nil
|
||||
}
|
||||
|
||||
if !strings.Contains(arg, "=") {
|
||||
return filters, ErrorBadFormat
|
||||
}
|
||||
|
||||
f := strings.SplitN(arg, "=", 2)
|
||||
name := strings.ToLower(strings.TrimSpace(f[0]))
|
||||
value := strings.TrimSpace(f[1])
|
||||
filters[name] = append(filters[name], value)
|
||||
|
||||
return filters, nil
|
||||
}
|
||||
|
||||
var ErrorBadFormat = errors.New("bad format of filter (expected name=value)")
|
||||
|
||||
// packs the Args into an string for easy transport from client to server
|
||||
func ToParam(a Args) (string, error) {
|
||||
// this way we don't URL encode {}, just empty space
|
||||
if len(a) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
// unpacks the filter Args
|
||||
func FromParam(p string) (Args, error) {
|
||||
args := Args{}
|
||||
if len(p) == 0 {
|
||||
return args, nil
|
||||
}
|
||||
if err := json.NewDecoder(strings.NewReader(p)).Decode(&args); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (filters Args) MatchKVList(field string, sources map[string]string) bool {
|
||||
fieldValues := filters[field]
|
||||
|
||||
//do not filter if there is no filter set or cannot determine filter
|
||||
if len(fieldValues) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if sources == nil || len(sources) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
outer:
|
||||
for _, name2match := range fieldValues {
|
||||
testKV := strings.SplitN(name2match, "=", 2)
|
||||
|
||||
for k, v := range sources {
|
||||
if len(testKV) == 1 {
|
||||
if k == testKV[0] {
|
||||
continue outer
|
||||
}
|
||||
} else if k == testKV[0] && v == testKV[1] {
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (filters Args) Match(field, source string) bool {
|
||||
fieldValues := filters[field]
|
||||
|
||||
//do not filter if there is no filter set or cannot determine filter
|
||||
if len(fieldValues) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, name2match := range fieldValues {
|
||||
match, err := regexp.MatchString(name2match, source)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if match {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
78
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse_test.go
generated
vendored
Normal file
78
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/filters/parse_test.go
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
package filters
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseArgs(t *testing.T) {
|
||||
// equivalent of `docker ps -f 'created=today' -f 'image.name=ubuntu*' -f 'image.name=*untu'`
|
||||
flagArgs := []string{
|
||||
"created=today",
|
||||
"image.name=ubuntu*",
|
||||
"image.name=*untu",
|
||||
}
|
||||
var (
|
||||
args = Args{}
|
||||
err error
|
||||
)
|
||||
for i := range flagArgs {
|
||||
args, err = ParseFlag(flagArgs[i], args)
|
||||
if err != nil {
|
||||
t.Errorf("failed to parse %s: %s", flagArgs[i], err)
|
||||
}
|
||||
}
|
||||
if len(args["created"]) != 1 {
|
||||
t.Errorf("failed to set this arg")
|
||||
}
|
||||
if len(args["image.name"]) != 2 {
|
||||
t.Errorf("the args should have collapsed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParam(t *testing.T) {
|
||||
a := Args{
|
||||
"created": []string{"today"},
|
||||
"image.name": []string{"ubuntu*", "*untu"},
|
||||
}
|
||||
|
||||
v, err := ToParam(a)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal the filters: %s", err)
|
||||
}
|
||||
v1, err := FromParam(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s", err)
|
||||
}
|
||||
for key, vals := range v1 {
|
||||
if _, ok := a[key]; !ok {
|
||||
t.Errorf("could not find key %s in original set", key)
|
||||
}
|
||||
sort.Strings(vals)
|
||||
sort.Strings(a[key])
|
||||
if len(vals) != len(a[key]) {
|
||||
t.Errorf("value lengths ought to match")
|
||||
continue
|
||||
}
|
||||
for i := range vals {
|
||||
if vals[i] != a[key][i] {
|
||||
t.Errorf("expected %s, but got %s", a[key][i], vals[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
a := Args{}
|
||||
v, err := ToParam(a)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal the filters: %s", err)
|
||||
}
|
||||
v1, err := FromParam(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s", err)
|
||||
}
|
||||
if len(a) != len(v1) {
|
||||
t.Errorf("these should both be empty sets")
|
||||
}
|
||||
}
|
95
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go
generated
vendored
Normal file
95
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
// +build !windows
|
||||
|
||||
package kernel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type KernelVersionInfo struct {
|
||||
Kernel int
|
||||
Major int
|
||||
Minor int
|
||||
Flavor string
|
||||
}
|
||||
|
||||
func (k *KernelVersionInfo) String() string {
|
||||
return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, k.Flavor)
|
||||
}
|
||||
|
||||
// Compare two KernelVersionInfo struct.
|
||||
// Returns -1 if a < b, 0 if a == b, 1 it a > b
|
||||
func CompareKernelVersion(a, b *KernelVersionInfo) int {
|
||||
if a.Kernel < b.Kernel {
|
||||
return -1
|
||||
} else if a.Kernel > b.Kernel {
|
||||
return 1
|
||||
}
|
||||
|
||||
if a.Major < b.Major {
|
||||
return -1
|
||||
} else if a.Major > b.Major {
|
||||
return 1
|
||||
}
|
||||
|
||||
if a.Minor < b.Minor {
|
||||
return -1
|
||||
} else if a.Minor > b.Minor {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func GetKernelVersion() (*KernelVersionInfo, error) {
|
||||
var (
|
||||
err error
|
||||
)
|
||||
|
||||
uts, err := uname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
release := make([]byte, len(uts.Release))
|
||||
|
||||
i := 0
|
||||
for _, c := range uts.Release {
|
||||
release[i] = byte(c)
|
||||
i++
|
||||
}
|
||||
|
||||
// Remove the \x00 from the release for Atoi to parse correctly
|
||||
release = release[:bytes.IndexByte(release, 0)]
|
||||
|
||||
return ParseRelease(string(release))
|
||||
}
|
||||
|
||||
func ParseRelease(release string) (*KernelVersionInfo, error) {
|
||||
var (
|
||||
kernel, major, minor, parsed int
|
||||
flavor, partial string
|
||||
)
|
||||
|
||||
// Ignore error from Sscanf to allow an empty flavor. Instead, just
|
||||
// make sure we got all the version numbers.
|
||||
parsed, _ = fmt.Sscanf(release, "%d.%d%s", &kernel, &major, &partial)
|
||||
if parsed < 2 {
|
||||
return nil, errors.New("Can't parse kernel version " + release)
|
||||
}
|
||||
|
||||
// sometimes we have 3.12.25-gentoo, but sometimes we just have 3.12-1-amd64
|
||||
parsed, _ = fmt.Sscanf(partial, ".%d%s", &minor, &flavor)
|
||||
if parsed < 1 {
|
||||
flavor = partial
|
||||
}
|
||||
|
||||
return &KernelVersionInfo{
|
||||
Kernel: kernel,
|
||||
Major: major,
|
||||
Minor: minor,
|
||||
Flavor: flavor,
|
||||
}, nil
|
||||
}
|
61
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_test.go
generated
vendored
Normal file
61
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_test.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
package kernel
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func assertParseRelease(t *testing.T, release string, b *KernelVersionInfo, result int) {
|
||||
var (
|
||||
a *KernelVersionInfo
|
||||
)
|
||||
a, _ = ParseRelease(release)
|
||||
|
||||
if r := CompareKernelVersion(a, b); r != result {
|
||||
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
|
||||
}
|
||||
if a.Flavor != b.Flavor {
|
||||
t.Fatalf("Unexpected parsed kernel flavor. Found %s, expected %s", a.Flavor, b.Flavor)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRelease(t *testing.T) {
|
||||
assertParseRelease(t, "3.8.0", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, 0)
|
||||
assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0)
|
||||
assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0)
|
||||
assertParseRelease(t, "3.8.0-19-generic", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "-19-generic"}, 0)
|
||||
assertParseRelease(t, "3.12.8tag", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 8, Flavor: "tag"}, 0)
|
||||
assertParseRelease(t, "3.12-1-amd64", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 0, Flavor: "-1-amd64"}, 0)
|
||||
}
|
||||
|
||||
func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) {
|
||||
if r := CompareKernelVersion(a, b); r != result {
|
||||
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareKernelVersion(t *testing.T) {
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
0)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
-1)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
|
||||
1)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
0)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
1)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
-1)
|
||||
}
|
65
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go
generated
vendored
Normal file
65
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
package kernel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type KernelVersionInfo struct {
|
||||
kvi string
|
||||
major int
|
||||
minor int
|
||||
build int
|
||||
}
|
||||
|
||||
func (k *KernelVersionInfo) String() string {
|
||||
return fmt.Sprintf("%d.%d %d (%s)", k.major, k.minor, k.build, k.kvi)
|
||||
}
|
||||
|
||||
func GetKernelVersion() (*KernelVersionInfo, error) {
|
||||
|
||||
var (
|
||||
h syscall.Handle
|
||||
dwVersion uint32
|
||||
err error
|
||||
)
|
||||
|
||||
KVI := &KernelVersionInfo{"Unknown", 0, 0, 0}
|
||||
|
||||
if err = syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE,
|
||||
syscall.StringToUTF16Ptr(`SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\`),
|
||||
0,
|
||||
syscall.KEY_READ,
|
||||
&h); err != nil {
|
||||
return KVI, err
|
||||
}
|
||||
defer syscall.RegCloseKey(h)
|
||||
|
||||
var buf [1 << 10]uint16
|
||||
var typ uint32
|
||||
n := uint32(len(buf) * 2) // api expects array of bytes, not uint16
|
||||
|
||||
if err = syscall.RegQueryValueEx(h,
|
||||
syscall.StringToUTF16Ptr("BuildLabEx"),
|
||||
nil,
|
||||
&typ,
|
||||
(*byte)(unsafe.Pointer(&buf[0])),
|
||||
&n); err != nil {
|
||||
return KVI, err
|
||||
}
|
||||
|
||||
KVI.kvi = syscall.UTF16ToString(buf[:])
|
||||
|
||||
// Important - docker.exe MUST be manifested for this API to return
|
||||
// the correct information.
|
||||
if dwVersion, err = syscall.GetVersion(); err != nil {
|
||||
return KVI, err
|
||||
}
|
||||
|
||||
KVI.major = int(dwVersion & 0xFF)
|
||||
KVI.minor = int((dwVersion & 0XFF00) >> 8)
|
||||
KVI.build = int((dwVersion & 0xFFFF0000) >> 16)
|
||||
|
||||
return KVI, nil
|
||||
}
|
16
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_linux.go
generated
vendored
Normal file
16
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
package kernel
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type Utsname syscall.Utsname
|
||||
|
||||
func uname() (*syscall.Utsname, error) {
|
||||
uts := &syscall.Utsname{}
|
||||
|
||||
if err := syscall.Uname(uts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return uts, nil
|
||||
}
|
15
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_unsupported.go
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/uname_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
// +build !linux
|
||||
|
||||
package kernel
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Utsname struct {
|
||||
Release [65]byte
|
||||
}
|
||||
|
||||
func uname() (*Utsname, error) {
|
||||
return nil, errors.New("Kernel version detection is available only on linux")
|
||||
}
|
40
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_linux.go
generated
vendored
Normal file
40
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
package operatingsystem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
var (
|
||||
// file to use to detect if the daemon is running in a container
|
||||
proc1Cgroup = "/proc/1/cgroup"
|
||||
|
||||
// file to check to determine Operating System
|
||||
etcOsRelease = "/etc/os-release"
|
||||
)
|
||||
|
||||
func GetOperatingSystem() (string, error) {
|
||||
b, err := ioutil.ReadFile(etcOsRelease)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if i := bytes.Index(b, []byte("PRETTY_NAME")); i >= 0 {
|
||||
b = b[i+13:]
|
||||
return string(b[:bytes.IndexByte(b, '"')]), nil
|
||||
}
|
||||
return "", errors.New("PRETTY_NAME not found")
|
||||
}
|
||||
|
||||
func IsContainerized() (bool, error) {
|
||||
b, err := ioutil.ReadFile(proc1Cgroup)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, line := range bytes.Split(b, []byte{'\n'}) {
|
||||
if len(line) > 0 && !bytes.HasSuffix(line, []byte{'/'}) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
124
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_test.go
generated
vendored
Normal file
124
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_test.go
generated
vendored
Normal file
|
@ -0,0 +1,124 @@
|
|||
package operatingsystem
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetOperatingSystem(t *testing.T) {
|
||||
var (
|
||||
backup = etcOsRelease
|
||||
ubuntuTrusty = []byte(`NAME="Ubuntu"
|
||||
VERSION="14.04, Trusty Tahr"
|
||||
ID=ubuntu
|
||||
ID_LIKE=debian
|
||||
PRETTY_NAME="Ubuntu 14.04 LTS"
|
||||
VERSION_ID="14.04"
|
||||
HOME_URL="http://www.ubuntu.com/"
|
||||
SUPPORT_URL="http://help.ubuntu.com/"
|
||||
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`)
|
||||
gentoo = []byte(`NAME=Gentoo
|
||||
ID=gentoo
|
||||
PRETTY_NAME="Gentoo/Linux"
|
||||
ANSI_COLOR="1;32"
|
||||
HOME_URL="http://www.gentoo.org/"
|
||||
SUPPORT_URL="http://www.gentoo.org/main/en/support.xml"
|
||||
BUG_REPORT_URL="https://bugs.gentoo.org/"
|
||||
`)
|
||||
noPrettyName = []byte(`NAME="Ubuntu"
|
||||
VERSION="14.04, Trusty Tahr"
|
||||
ID=ubuntu
|
||||
ID_LIKE=debian
|
||||
VERSION_ID="14.04"
|
||||
HOME_URL="http://www.ubuntu.com/"
|
||||
SUPPORT_URL="http://help.ubuntu.com/"
|
||||
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`)
|
||||
)
|
||||
|
||||
dir := os.TempDir()
|
||||
etcOsRelease = filepath.Join(dir, "etcOsRelease")
|
||||
|
||||
defer func() {
|
||||
os.Remove(etcOsRelease)
|
||||
etcOsRelease = backup
|
||||
}()
|
||||
|
||||
for expect, osRelease := range map[string][]byte{
|
||||
"Ubuntu 14.04 LTS": ubuntuTrusty,
|
||||
"Gentoo/Linux": gentoo,
|
||||
"": noPrettyName,
|
||||
} {
|
||||
if err := ioutil.WriteFile(etcOsRelease, osRelease, 0600); err != nil {
|
||||
t.Fatalf("failed to write to %s: %v", etcOsRelease, err)
|
||||
}
|
||||
s, err := GetOperatingSystem()
|
||||
if s != expect {
|
||||
if expect == "" {
|
||||
t.Fatalf("Expected error 'PRETTY_NAME not found', but got %v", err)
|
||||
} else {
|
||||
t.Fatalf("Expected '%s', but got '%s'. Err=%v", expect, s, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsContainerized(t *testing.T) {
|
||||
var (
|
||||
backup = proc1Cgroup
|
||||
nonContainerizedProc1Cgroup = []byte(`14:name=systemd:/
|
||||
13:hugetlb:/
|
||||
12:net_prio:/
|
||||
11:perf_event:/
|
||||
10:bfqio:/
|
||||
9:blkio:/
|
||||
8:net_cls:/
|
||||
7:freezer:/
|
||||
6:devices:/
|
||||
5:memory:/
|
||||
4:cpuacct:/
|
||||
3:cpu:/
|
||||
2:cpuset:/
|
||||
`)
|
||||
containerizedProc1Cgroup = []byte(`9:perf_event:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
||||
8:blkio:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
||||
7:net_cls:/
|
||||
6:freezer:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
||||
5:devices:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
||||
4:memory:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
||||
3:cpuacct:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
||||
2:cpu:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d
|
||||
1:cpuset:/`)
|
||||
)
|
||||
|
||||
dir := os.TempDir()
|
||||
proc1Cgroup = filepath.Join(dir, "proc1Cgroup")
|
||||
|
||||
defer func() {
|
||||
os.Remove(proc1Cgroup)
|
||||
proc1Cgroup = backup
|
||||
}()
|
||||
|
||||
if err := ioutil.WriteFile(proc1Cgroup, nonContainerizedProc1Cgroup, 0600); err != nil {
|
||||
t.Fatalf("failed to write to %s: %v", proc1Cgroup, err)
|
||||
}
|
||||
inContainer, err := IsContainerized()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if inContainer {
|
||||
t.Fatal("Wrongly assuming containerized")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(proc1Cgroup, containerizedProc1Cgroup, 0600); err != nil {
|
||||
t.Fatalf("failed to write to %s: %v", proc1Cgroup, err)
|
||||
}
|
||||
inContainer, err = IsContainerized()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !inContainer {
|
||||
t.Fatal("Wrongly assuming non-containerized")
|
||||
}
|
||||
}
|
47
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_windows.go
generated
vendored
Normal file
47
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/operatingsystem/operatingsystem_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
package operatingsystem
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// See https://code.google.com/p/go/source/browse/src/pkg/mime/type_windows.go?r=d14520ac25bf6940785aabb71f5be453a286f58c
|
||||
// for a similar sample
|
||||
|
||||
func GetOperatingSystem() (string, error) {
|
||||
|
||||
var h syscall.Handle
|
||||
|
||||
// Default return value
|
||||
ret := "Unknown Operating System"
|
||||
|
||||
if err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE,
|
||||
syscall.StringToUTF16Ptr(`SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\`),
|
||||
0,
|
||||
syscall.KEY_READ,
|
||||
&h); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
defer syscall.RegCloseKey(h)
|
||||
|
||||
var buf [1 << 10]uint16
|
||||
var typ uint32
|
||||
n := uint32(len(buf) * 2) // api expects array of bytes, not uint16
|
||||
|
||||
if err := syscall.RegQueryValueEx(h,
|
||||
syscall.StringToUTF16Ptr("ProductName"),
|
||||
nil,
|
||||
&typ,
|
||||
(*byte)(unsafe.Pointer(&buf[0])),
|
||||
&n); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
ret = syscall.UTF16ToString(buf[:])
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// No-op on Windows
|
||||
func IsContainerized() (bool, error) {
|
||||
return false, nil
|
||||
}
|
|
@ -2,6 +2,7 @@ package parsers
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
@ -10,7 +11,12 @@ import (
|
|||
func ParseHost(defaultTCPAddr, defaultUnixAddr, addr string) (string, error) {
|
||||
addr = strings.TrimSpace(addr)
|
||||
if addr == "" {
|
||||
addr = fmt.Sprintf("unix://%s", defaultUnixAddr)
|
||||
if runtime.GOOS != "windows" {
|
||||
addr = fmt.Sprintf("unix://%s", defaultUnixAddr)
|
||||
} else {
|
||||
// Note - defaultTCPAddr already includes tcp:// prefix
|
||||
addr = fmt.Sprintf("%s", defaultTCPAddr)
|
||||
}
|
||||
}
|
||||
addrParts := strings.Split(addr, "://")
|
||||
if len(addrParts) == 1 {
|
||||
|
@ -135,3 +141,17 @@ func ParsePortRange(ports string) (uint64, uint64, error) {
|
|||
}
|
||||
return start, end, nil
|
||||
}
|
||||
|
||||
func ParseLink(val string) (string, string, error) {
|
||||
if val == "" {
|
||||
return "", "", fmt.Errorf("empty string specified for links")
|
||||
}
|
||||
arr := strings.Split(val, ":")
|
||||
if len(arr) > 2 {
|
||||
return "", "", fmt.Errorf("bad format for links: %s", val)
|
||||
}
|
||||
if len(arr) == 1 {
|
||||
return val, val, nil
|
||||
}
|
||||
return arr[0], arr[1], nil
|
||||
}
|
||||
|
|
157
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/parsers_test.go
generated
vendored
Normal file
157
Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/parsers_test.go
generated
vendored
Normal file
|
@ -0,0 +1,157 @@
|
|||
package parsers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseHost(t *testing.T) {
|
||||
var (
|
||||
defaultHttpHost = "127.0.0.1"
|
||||
defaultUnix = "/var/run/docker.sock"
|
||||
)
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "0.0.0.0"); err == nil {
|
||||
t.Errorf("tcp 0.0.0.0 address expected error return, but err == nil, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "tcp://"); err == nil {
|
||||
t.Errorf("default tcp:// address expected error return, but err == nil, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "0.0.0.1:5555"); err != nil || addr != "tcp://0.0.0.1:5555" {
|
||||
t.Errorf("0.0.0.1:5555 -> expected tcp://0.0.0.1:5555, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, ":6666"); err != nil || addr != "tcp://127.0.0.1:6666" {
|
||||
t.Errorf(":6666 -> expected tcp://127.0.0.1:6666, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "tcp://:7777"); err != nil || addr != "tcp://127.0.0.1:7777" {
|
||||
t.Errorf("tcp://:7777 -> expected tcp://127.0.0.1:7777, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, ""); err != nil || addr != "unix:///var/run/docker.sock" {
|
||||
t.Errorf("empty argument -> expected unix:///var/run/docker.sock, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "unix:///var/run/docker.sock"); err != nil || addr != "unix:///var/run/docker.sock" {
|
||||
t.Errorf("unix:///var/run/docker.sock -> expected unix:///var/run/docker.sock, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "unix://"); err != nil || addr != "unix:///var/run/docker.sock" {
|
||||
t.Errorf("unix:///var/run/docker.sock -> expected unix:///var/run/docker.sock, got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "udp://127.0.0.1"); err == nil {
|
||||
t.Errorf("udp protocol address expected error return, but err == nil. Got %s", addr)
|
||||
}
|
||||
if addr, err := ParseHost(defaultHttpHost, defaultUnix, "udp://127.0.0.1:2375"); err == nil {
|
||||
t.Errorf("udp protocol address expected error return, but err == nil. Got %s", addr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRepositoryTag(t *testing.T) {
|
||||
if repo, tag := ParseRepositoryTag("root"); repo != "root" || tag != "" {
|
||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "", repo, tag)
|
||||
}
|
||||
if repo, tag := ParseRepositoryTag("root:tag"); repo != "root" || tag != "tag" {
|
||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "root", "tag", repo, tag)
|
||||
}
|
||||
if repo, digest := ParseRepositoryTag("root@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); repo != "root" || digest != "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {
|
||||
t.Errorf("Expected repo: '%s' and digest: '%s', got '%s' and '%s'", "root", "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", repo, digest)
|
||||
}
|
||||
if repo, tag := ParseRepositoryTag("user/repo"); repo != "user/repo" || tag != "" {
|
||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "", repo, tag)
|
||||
}
|
||||
if repo, tag := ParseRepositoryTag("user/repo:tag"); repo != "user/repo" || tag != "tag" {
|
||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "user/repo", "tag", repo, tag)
|
||||
}
|
||||
if repo, digest := ParseRepositoryTag("user/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); repo != "user/repo" || digest != "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {
|
||||
t.Errorf("Expected repo: '%s' and digest: '%s', got '%s' and '%s'", "user/repo", "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", repo, digest)
|
||||
}
|
||||
if repo, tag := ParseRepositoryTag("url:5000/repo"); repo != "url:5000/repo" || tag != "" {
|
||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "", repo, tag)
|
||||
}
|
||||
if repo, tag := ParseRepositoryTag("url:5000/repo:tag"); repo != "url:5000/repo" || tag != "tag" {
|
||||
t.Errorf("Expected repo: '%s' and tag: '%s', got '%s' and '%s'", "url:5000/repo", "tag", repo, tag)
|
||||
}
|
||||
if repo, digest := ParseRepositoryTag("url:5000/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); repo != "url:5000/repo" || digest != "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {
|
||||
t.Errorf("Expected repo: '%s' and digest: '%s', got '%s' and '%s'", "url:5000/repo", "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", repo, digest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePortMapping(t *testing.T) {
|
||||
data, err := PartParser("ip:public:private", "192.168.1.1:80:8080")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(data) != 3 {
|
||||
t.FailNow()
|
||||
}
|
||||
if data["ip"] != "192.168.1.1" {
|
||||
t.Fail()
|
||||
}
|
||||
if data["public"] != "80" {
|
||||
t.Fail()
|
||||
}
|
||||
if data["private"] != "8080" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePortRange(t *testing.T) {
|
||||
if start, end, err := ParsePortRange("8000-8080"); err != nil || start != 8000 || end != 8080 {
|
||||
t.Fatalf("Error: %s or Expecting {start,end} values {8000,8080} but found {%d,%d}.", err, start, end)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePortRangeIncorrectRange(t *testing.T) {
|
||||
if _, _, err := ParsePortRange("9000-8080"); err == nil || !strings.Contains(err.Error(), "Invalid range specified for the Port") {
|
||||
t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePortRangeIncorrectEndRange(t *testing.T) {
|
||||
if _, _, err := ParsePortRange("8000-a"); err == nil || !strings.Contains(err.Error(), "invalid syntax") {
|
||||
t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
|
||||
}
|
||||
|
||||
if _, _, err := ParsePortRange("8000-30a"); err == nil || !strings.Contains(err.Error(), "invalid syntax") {
|
||||
t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePortRangeIncorrectStartRange(t *testing.T) {
|
||||
if _, _, err := ParsePortRange("a-8000"); err == nil || !strings.Contains(err.Error(), "invalid syntax") {
|
||||
t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
|
||||
}
|
||||
|
||||
if _, _, err := ParsePortRange("30a-8000"); err == nil || !strings.Contains(err.Error(), "invalid syntax") {
|
||||
t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLink(t *testing.T) {
|
||||
name, alias, err := ParseLink("name:alias")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected not to error out on a valid name:alias format but got: %v", err)
|
||||
}
|
||||
if name != "name" {
|
||||
t.Fatalf("Link name should have been name, got %s instead", name)
|
||||
}
|
||||
if alias != "alias" {
|
||||
t.Fatalf("Link alias should have been alias, got %s instead", alias)
|
||||
}
|
||||
// short format definition
|
||||
name, alias, err = ParseLink("name")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected not to error out on a valid name only format but got: %v", err)
|
||||
}
|
||||
if name != "name" {
|
||||
t.Fatalf("Link name should have been name, got %s instead", name)
|
||||
}
|
||||
if alias != "name" {
|
||||
t.Fatalf("Link alias should have been name, got %s instead", alias)
|
||||
}
|
||||
// empty string link definition is not allowed
|
||||
if _, _, err := ParseLink(""); err == nil || !strings.Contains(err.Error(), "empty string specified for links") {
|
||||
t.Fatalf("Expected error 'empty string specified for links' but got: %v", err)
|
||||
}
|
||||
// more than two colons are not allowed
|
||||
if _, _, err := ParseLink("link:alias:wrong"); err == nil || !strings.Contains(err.Error(), "bad format for links: link:alias:wrong") {
|
||||
t.Fatalf("Expected error 'bad format for links: link:alias:wrong' but got: %v", err)
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
package term
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Consts for Get/SetConsoleMode function
|
||||
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
|
||||
ENABLE_ECHO_INPUT = 0x0004
|
||||
ENABLE_INSERT_MODE = 0x0020
|
||||
ENABLE_LINE_INPUT = 0x0002
|
||||
ENABLE_MOUSE_INPUT = 0x0010
|
||||
ENABLE_PROCESSED_INPUT = 0x0001
|
||||
ENABLE_QUICK_EDIT_MODE = 0x0040
|
||||
ENABLE_WINDOW_INPUT = 0x0008
|
||||
// If parameter is a screen buffer handle, additional values
|
||||
ENABLE_PROCESSED_OUTPUT = 0x0001
|
||||
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
|
||||
)
|
||||
|
||||
var kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
var (
|
||||
setConsoleModeProc = kernel32DLL.NewProc("SetConsoleMode")
|
||||
getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
|
||||
)
|
||||
|
||||
func GetConsoleMode(fileDesc uintptr) (uint32, error) {
|
||||
var mode uint32
|
||||
err := syscall.GetConsoleMode(syscall.Handle(fileDesc), &mode)
|
||||
return mode, err
|
||||
}
|
||||
|
||||
func SetConsoleMode(fileDesc uintptr, mode uint32) error {
|
||||
r, _, err := setConsoleModeProc.Call(fileDesc, uintptr(mode), 0)
|
||||
if r == 0 {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return syscall.EINVAL
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// types for calling GetConsoleScreenBufferInfo
|
||||
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx
|
||||
type (
|
||||
SHORT int16
|
||||
|
||||
SMALL_RECT struct {
|
||||
Left SHORT
|
||||
Top SHORT
|
||||
Right SHORT
|
||||
Bottom SHORT
|
||||
}
|
||||
|
||||
COORD struct {
|
||||
X SHORT
|
||||
Y SHORT
|
||||
}
|
||||
|
||||
WORD uint16
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO struct {
|
||||
dwSize COORD
|
||||
dwCursorPosition COORD
|
||||
wAttributes WORD
|
||||
srWindow SMALL_RECT
|
||||
dwMaximumWindowSize COORD
|
||||
}
|
||||
)
|
||||
|
||||
func GetConsoleScreenBufferInfo(fileDesc uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) {
|
||||
var info CONSOLE_SCREEN_BUFFER_INFO
|
||||
r, _, err := getConsoleScreenBufferInfoProc.Call(uintptr(fileDesc), uintptr(unsafe.Pointer(&info)), 0)
|
||||
if r == 0 {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
return &info, nil
|
||||
}
|
|
@ -24,6 +24,7 @@ func MakeRaw(fd uintptr) (*State, error) {
|
|||
newState := oldState.termios
|
||||
|
||||
C.cfmakeraw((*C.struct_termios)(unsafe.Pointer(&newState)))
|
||||
newState.Oflag = newState.Oflag | C.OPOST
|
||||
if err := tcset(fd, &newState); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ package term
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
@ -25,6 +26,20 @@ type Winsize struct {
|
|||
y uint16
|
||||
}
|
||||
|
||||
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
|
||||
return os.Stdin, os.Stdout, os.Stderr
|
||||
}
|
||||
|
||||
func GetFdInfo(in interface{}) (uintptr, bool) {
|
||||
var inFd uintptr
|
||||
var isTerminalIn bool
|
||||
if file, ok := in.(*os.File); ok {
|
||||
inFd = file.Fd()
|
||||
isTerminalIn = IsTerminal(inFd)
|
||||
}
|
||||
return inFd, isTerminalIn
|
||||
}
|
||||
|
||||
func GetWinsize(fd uintptr) (*Winsize, error) {
|
||||
ws := &Winsize{}
|
||||
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
// +build windows
|
||||
|
||||
package term
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/term/winconsole"
|
||||
)
|
||||
|
||||
// State holds the console mode for the terminal.
|
||||
type State struct {
|
||||
mode uint32
|
||||
}
|
||||
|
||||
// Winsize is used for window size.
|
||||
type Winsize struct {
|
||||
Height uint16
|
||||
Width uint16
|
||||
|
@ -13,75 +22,115 @@ type Winsize struct {
|
|||
y uint16
|
||||
}
|
||||
|
||||
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
|
||||
switch {
|
||||
case os.Getenv("ConEmuANSI") == "ON":
|
||||
// The ConEmu shell emulates ANSI well by default.
|
||||
return os.Stdin, os.Stdout, os.Stderr
|
||||
case os.Getenv("MSYSTEM") != "":
|
||||
// MSYS (mingw) does not emulate ANSI well.
|
||||
return winconsole.WinConsoleStreams()
|
||||
default:
|
||||
return winconsole.WinConsoleStreams()
|
||||
}
|
||||
}
|
||||
|
||||
// GetFdInfo returns file descriptor and bool indicating whether the file is a terminal.
|
||||
func GetFdInfo(in interface{}) (uintptr, bool) {
|
||||
return winconsole.GetHandleInfo(in)
|
||||
}
|
||||
|
||||
// GetWinsize retrieves the window size of the terminal connected to the passed file descriptor.
|
||||
func GetWinsize(fd uintptr) (*Winsize, error) {
|
||||
ws := &Winsize{}
|
||||
var info *CONSOLE_SCREEN_BUFFER_INFO
|
||||
info, err := GetConsoleScreenBufferInfo(fd)
|
||||
info, err := winconsole.GetConsoleScreenBufferInfo(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ws.Height = uint16(info.srWindow.Right - info.srWindow.Left + 1)
|
||||
ws.Width = uint16(info.srWindow.Bottom - info.srWindow.Top + 1)
|
||||
|
||||
ws.x = 0 // todo azlinux -- this is the pixel size of the Window, and not currently used by any caller
|
||||
ws.y = 0
|
||||
|
||||
return ws, nil
|
||||
// TODO(azlinux): Set the pixel width / height of the console (currently unused by any caller)
|
||||
return &Winsize{
|
||||
Width: uint16(info.Window.Right - info.Window.Left + 1),
|
||||
Height: uint16(info.Window.Bottom - info.Window.Top + 1),
|
||||
x: 0,
|
||||
y: 0}, nil
|
||||
}
|
||||
|
||||
// SetWinsize sets the size of the given terminal connected to the passed file descriptor.
|
||||
func SetWinsize(fd uintptr, ws *Winsize) error {
|
||||
// TODO(azlinux): Implement SetWinsize
|
||||
logrus.Debugf("[windows] SetWinsize: WARNING -- Unsupported method invoked")
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
_, e := GetConsoleMode(fd)
|
||||
return e == nil
|
||||
return winconsole.IsConsole(fd)
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// RestoreTerminal restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func RestoreTerminal(fd uintptr, state *State) error {
|
||||
return SetConsoleMode(fd, state.mode)
|
||||
return winconsole.SetConsoleMode(fd, state.mode)
|
||||
}
|
||||
|
||||
// SaveState saves the state of the terminal connected to the given file descriptor.
|
||||
func SaveState(fd uintptr) (*State, error) {
|
||||
mode, e := GetConsoleMode(fd)
|
||||
mode, e := winconsole.GetConsoleMode(fd)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
return &State{mode}, nil
|
||||
}
|
||||
|
||||
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx for these flag settings
|
||||
// DisableEcho disables echo for the terminal connected to the given file descriptor.
|
||||
// -- See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
|
||||
func DisableEcho(fd uintptr, state *State) error {
|
||||
state.mode &^= (ENABLE_ECHO_INPUT)
|
||||
state.mode |= (ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)
|
||||
return SetConsoleMode(fd, state.mode)
|
||||
mode := state.mode
|
||||
mode &^= winconsole.ENABLE_ECHO_INPUT
|
||||
mode |= winconsole.ENABLE_PROCESSED_INPUT | winconsole.ENABLE_LINE_INPUT
|
||||
// TODO(azlinux): Core code registers a goroutine to catch os.Interrupt and reset the terminal state.
|
||||
return winconsole.SetConsoleMode(fd, mode)
|
||||
}
|
||||
|
||||
// SetRawTerminal puts the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
func SetRawTerminal(fd uintptr) (*State, error) {
|
||||
oldState, err := MakeRaw(fd)
|
||||
state, err := MakeRaw(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO (azlinux): implement handling interrupt and restore state of terminal
|
||||
return oldState, err
|
||||
// TODO(azlinux): Core code registers a goroutine to catch os.Interrupt and reset the terminal state.
|
||||
return state, err
|
||||
}
|
||||
|
||||
// MakeRaw puts the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
func MakeRaw(fd uintptr) (*State, error) {
|
||||
var state *State
|
||||
state, err := SaveState(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx for these flag settings
|
||||
state.mode &^= (ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)
|
||||
err = SetConsoleMode(fd, state.mode)
|
||||
// See
|
||||
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
|
||||
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
|
||||
mode := state.mode
|
||||
|
||||
// Disable these modes
|
||||
mode &^= winconsole.ENABLE_ECHO_INPUT
|
||||
mode &^= winconsole.ENABLE_LINE_INPUT
|
||||
mode &^= winconsole.ENABLE_MOUSE_INPUT
|
||||
mode &^= winconsole.ENABLE_WINDOW_INPUT
|
||||
mode &^= winconsole.ENABLE_PROCESSED_INPUT
|
||||
|
||||
// Enable these modes
|
||||
mode |= winconsole.ENABLE_EXTENDED_FLAGS
|
||||
mode |= winconsole.ENABLE_INSERT_MODE
|
||||
mode |= winconsole.ENABLE_QUICK_EDIT_MODE
|
||||
|
||||
err = winconsole.SetConsoleMode(fd, mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
1053
Godeps/_workspace/src/github.com/docker/docker/pkg/term/winconsole/console_windows.go
generated
vendored
Normal file
1053
Godeps/_workspace/src/github.com/docker/docker/pkg/term/winconsole/console_windows.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
232
Godeps/_workspace/src/github.com/docker/docker/pkg/term/winconsole/console_windows_test.go
generated
vendored
Normal file
232
Godeps/_workspace/src/github.com/docker/docker/pkg/term/winconsole/console_windows_test.go
generated
vendored
Normal file
|
@ -0,0 +1,232 @@
|
|||
// +build windows
|
||||
|
||||
package winconsole
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func helpsTestParseInt16OrDefault(t *testing.T, expectedValue int16, shouldFail bool, input string, defaultValue int16, format string, args ...string) {
|
||||
value, err := parseInt16OrDefault(input, defaultValue)
|
||||
if nil != err && !shouldFail {
|
||||
t.Errorf("Unexpected error returned %v", err)
|
||||
t.Errorf(format, args)
|
||||
}
|
||||
if nil == err && shouldFail {
|
||||
t.Errorf("Should have failed as expected\n\tReturned value = %d", value)
|
||||
t.Errorf(format, args)
|
||||
}
|
||||
if expectedValue != value {
|
||||
t.Errorf("The value returned does not match expected\n\tExpected:%v\n\t:Actual%v", expectedValue, value)
|
||||
t.Errorf(format, args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseInt16OrDefault(t *testing.T) {
|
||||
// empty string
|
||||
helpsTestParseInt16OrDefault(t, 0, false, "", 0, "Empty string returns default")
|
||||
helpsTestParseInt16OrDefault(t, 2, false, "", 2, "Empty string returns default")
|
||||
|
||||
// normal case
|
||||
helpsTestParseInt16OrDefault(t, 0, false, "0", 0, "0 handled correctly")
|
||||
helpsTestParseInt16OrDefault(t, 111, false, "111", 2, "Normal")
|
||||
helpsTestParseInt16OrDefault(t, 111, false, "+111", 2, "+N")
|
||||
helpsTestParseInt16OrDefault(t, -111, false, "-111", 2, "-N")
|
||||
helpsTestParseInt16OrDefault(t, 0, false, "+0", 11, "+0")
|
||||
helpsTestParseInt16OrDefault(t, 0, false, "-0", 12, "-0")
|
||||
|
||||
// ill formed strings
|
||||
helpsTestParseInt16OrDefault(t, 0, true, "abc", 0, "Invalid string")
|
||||
helpsTestParseInt16OrDefault(t, 42, true, "+= 23", 42, "Invalid string")
|
||||
helpsTestParseInt16OrDefault(t, 42, true, "123.45", 42, "float like")
|
||||
|
||||
}
|
||||
|
||||
func helpsTestGetNumberOfChars(t *testing.T, expected uint32, fromCoord COORD, toCoord COORD, screenSize COORD, format string, args ...interface{}) {
|
||||
actual := getNumberOfChars(fromCoord, toCoord, screenSize)
|
||||
mesg := fmt.Sprintf(format, args)
|
||||
assertTrue(t, expected == actual, fmt.Sprintf("%s Expected=%d, Actual=%d, Parameters = { fromCoord=%+v, toCoord=%+v, screenSize=%+v", mesg, expected, actual, fromCoord, toCoord, screenSize))
|
||||
}
|
||||
|
||||
func TestGetNumberOfChars(t *testing.T) {
|
||||
// Note: The columns and lines are 0 based
|
||||
// Also that interval is "inclusive" means will have both start and end chars
|
||||
// This test only tests the number opf characters being written
|
||||
|
||||
// all four corners
|
||||
maxWindow := COORD{X: 80, Y: 50}
|
||||
leftTop := COORD{X: 0, Y: 0}
|
||||
rightTop := COORD{X: 79, Y: 0}
|
||||
leftBottom := COORD{X: 0, Y: 49}
|
||||
rightBottom := COORD{X: 79, Y: 49}
|
||||
|
||||
// same position
|
||||
helpsTestGetNumberOfChars(t, 1, COORD{X: 1, Y: 14}, COORD{X: 1, Y: 14}, COORD{X: 80, Y: 50}, "Same position random line")
|
||||
|
||||
// four corners
|
||||
helpsTestGetNumberOfChars(t, 1, leftTop, leftTop, maxWindow, "Same position- leftTop")
|
||||
helpsTestGetNumberOfChars(t, 1, rightTop, rightTop, maxWindow, "Same position- rightTop")
|
||||
helpsTestGetNumberOfChars(t, 1, leftBottom, leftBottom, maxWindow, "Same position- leftBottom")
|
||||
helpsTestGetNumberOfChars(t, 1, rightBottom, rightBottom, maxWindow, "Same position- rightBottom")
|
||||
|
||||
// from this char to next char on same line
|
||||
helpsTestGetNumberOfChars(t, 2, COORD{X: 0, Y: 0}, COORD{X: 1, Y: 0}, maxWindow, "Next position on same line")
|
||||
helpsTestGetNumberOfChars(t, 2, COORD{X: 1, Y: 14}, COORD{X: 2, Y: 14}, maxWindow, "Next position on same line")
|
||||
|
||||
// from this char to next 10 chars on same line
|
||||
helpsTestGetNumberOfChars(t, 11, COORD{X: 0, Y: 0}, COORD{X: 10, Y: 0}, maxWindow, "Next position on same line")
|
||||
helpsTestGetNumberOfChars(t, 11, COORD{X: 1, Y: 14}, COORD{X: 11, Y: 14}, maxWindow, "Next position on same line")
|
||||
|
||||
helpsTestGetNumberOfChars(t, 5, COORD{X: 3, Y: 11}, COORD{X: 7, Y: 11}, maxWindow, "To and from on same line")
|
||||
|
||||
helpsTestGetNumberOfChars(t, 8, COORD{X: 0, Y: 34}, COORD{X: 7, Y: 34}, maxWindow, "Start of line to middle")
|
||||
helpsTestGetNumberOfChars(t, 4, COORD{X: 76, Y: 34}, COORD{X: 79, Y: 34}, maxWindow, "Middle to end of line")
|
||||
|
||||
// multiple lines - 1
|
||||
helpsTestGetNumberOfChars(t, 81, COORD{X: 0, Y: 0}, COORD{X: 0, Y: 1}, maxWindow, "one line below same X")
|
||||
helpsTestGetNumberOfChars(t, 81, COORD{X: 10, Y: 10}, COORD{X: 10, Y: 11}, maxWindow, "one line below same X")
|
||||
|
||||
// multiple lines - 2
|
||||
helpsTestGetNumberOfChars(t, 161, COORD{X: 0, Y: 0}, COORD{X: 0, Y: 2}, maxWindow, "one line below same X")
|
||||
helpsTestGetNumberOfChars(t, 161, COORD{X: 10, Y: 10}, COORD{X: 10, Y: 12}, maxWindow, "one line below same X")
|
||||
|
||||
// multiple lines - 3
|
||||
helpsTestGetNumberOfChars(t, 241, COORD{X: 0, Y: 0}, COORD{X: 0, Y: 3}, maxWindow, "one line below same X")
|
||||
helpsTestGetNumberOfChars(t, 241, COORD{X: 10, Y: 10}, COORD{X: 10, Y: 13}, maxWindow, "one line below same X")
|
||||
|
||||
// full line
|
||||
helpsTestGetNumberOfChars(t, 80, COORD{X: 0, Y: 0}, COORD{X: 79, Y: 0}, maxWindow, "Full line - first")
|
||||
helpsTestGetNumberOfChars(t, 80, COORD{X: 0, Y: 23}, COORD{X: 79, Y: 23}, maxWindow, "Full line - random")
|
||||
helpsTestGetNumberOfChars(t, 80, COORD{X: 0, Y: 49}, COORD{X: 79, Y: 49}, maxWindow, "Full line - last")
|
||||
|
||||
// full screen
|
||||
helpsTestGetNumberOfChars(t, 80*50, leftTop, rightBottom, maxWindow, "full screen")
|
||||
|
||||
helpsTestGetNumberOfChars(t, 80*50-1, COORD{X: 1, Y: 0}, rightBottom, maxWindow, "dropping first char to, end of screen")
|
||||
helpsTestGetNumberOfChars(t, 80*50-2, COORD{X: 2, Y: 0}, rightBottom, maxWindow, "dropping first two char to, end of screen")
|
||||
|
||||
helpsTestGetNumberOfChars(t, 80*50-1, leftTop, COORD{X: 78, Y: 49}, maxWindow, "from start of screen, till last char-1")
|
||||
helpsTestGetNumberOfChars(t, 80*50-2, leftTop, COORD{X: 77, Y: 49}, maxWindow, "from start of screen, till last char-2")
|
||||
|
||||
helpsTestGetNumberOfChars(t, 80*50-5, COORD{X: 4, Y: 0}, COORD{X: 78, Y: 49}, COORD{X: 80, Y: 50}, "from start of screen+4, till last char-1")
|
||||
helpsTestGetNumberOfChars(t, 80*50-6, COORD{X: 4, Y: 0}, COORD{X: 77, Y: 49}, COORD{X: 80, Y: 50}, "from start of screen+4, till last char-2")
|
||||
}
|
||||
|
||||
var allForeground = []int16{
|
||||
ANSI_FOREGROUND_BLACK,
|
||||
ANSI_FOREGROUND_RED,
|
||||
ANSI_FOREGROUND_GREEN,
|
||||
ANSI_FOREGROUND_YELLOW,
|
||||
ANSI_FOREGROUND_BLUE,
|
||||
ANSI_FOREGROUND_MAGENTA,
|
||||
ANSI_FOREGROUND_CYAN,
|
||||
ANSI_FOREGROUND_WHITE,
|
||||
ANSI_FOREGROUND_DEFAULT,
|
||||
}
|
||||
var allBackground = []int16{
|
||||
ANSI_BACKGROUND_BLACK,
|
||||
ANSI_BACKGROUND_RED,
|
||||
ANSI_BACKGROUND_GREEN,
|
||||
ANSI_BACKGROUND_YELLOW,
|
||||
ANSI_BACKGROUND_BLUE,
|
||||
ANSI_BACKGROUND_MAGENTA,
|
||||
ANSI_BACKGROUND_CYAN,
|
||||
ANSI_BACKGROUND_WHITE,
|
||||
ANSI_BACKGROUND_DEFAULT,
|
||||
}
|
||||
|
||||
func maskForeground(flag WORD) WORD {
|
||||
return flag & FOREGROUND_MASK_UNSET
|
||||
}
|
||||
|
||||
func onlyForeground(flag WORD) WORD {
|
||||
return flag & FOREGROUND_MASK_SET
|
||||
}
|
||||
|
||||
func maskBackground(flag WORD) WORD {
|
||||
return flag & BACKGROUND_MASK_UNSET
|
||||
}
|
||||
|
||||
func onlyBackground(flag WORD) WORD {
|
||||
return flag & BACKGROUND_MASK_SET
|
||||
}
|
||||
|
||||
func helpsTestGetWindowsTextAttributeForAnsiValue(t *testing.T, oldValue WORD /*, expected WORD*/, ansi int16, onlyMask WORD, restMask WORD) WORD {
|
||||
actual, err := getWindowsTextAttributeForAnsiValue(oldValue, FOREGROUND_MASK_SET, ansi)
|
||||
assertTrue(t, nil == err, "Should be no error")
|
||||
// assert that other bits are not affected
|
||||
if 0 != oldValue {
|
||||
assertTrue(t, (actual&restMask) == (oldValue&restMask), "The operation should not have affected other bits actual=%X oldValue=%X ansi=%d", actual, oldValue, ansi)
|
||||
}
|
||||
return actual
|
||||
}
|
||||
|
||||
func TestBackgroundForAnsiValue(t *testing.T) {
|
||||
// Check that nothing else changes
|
||||
// background changes
|
||||
for _, state1 := range allBackground {
|
||||
for _, state2 := range allBackground {
|
||||
flag := WORD(0)
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
|
||||
}
|
||||
}
|
||||
// cummulative bcakground changes
|
||||
for _, state1 := range allBackground {
|
||||
flag := WORD(0)
|
||||
for _, state2 := range allBackground {
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
|
||||
}
|
||||
}
|
||||
// change background after foreground
|
||||
for _, state1 := range allForeground {
|
||||
for _, state2 := range allBackground {
|
||||
flag := WORD(0)
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
|
||||
}
|
||||
}
|
||||
// change background after change cumulative
|
||||
for _, state1 := range allForeground {
|
||||
flag := WORD(0)
|
||||
for _, state2 := range allBackground {
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestForegroundForAnsiValue(t *testing.T) {
|
||||
// Check that nothing else changes
|
||||
for _, state1 := range allForeground {
|
||||
for _, state2 := range allForeground {
|
||||
flag := WORD(0)
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
|
||||
}
|
||||
}
|
||||
|
||||
for _, state1 := range allForeground {
|
||||
flag := WORD(0)
|
||||
for _, state2 := range allForeground {
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
|
||||
}
|
||||
}
|
||||
for _, state1 := range allBackground {
|
||||
for _, state2 := range allForeground {
|
||||
flag := WORD(0)
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
|
||||
}
|
||||
}
|
||||
for _, state1 := range allBackground {
|
||||
flag := WORD(0)
|
||||
for _, state2 := range allForeground {
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state1, BACKGROUND_MASK_SET, BACKGROUND_MASK_UNSET)
|
||||
flag = helpsTestGetWindowsTextAttributeForAnsiValue(t, flag, state2, FOREGROUND_MASK_SET, FOREGROUND_MASK_UNSET)
|
||||
}
|
||||
}
|
||||
}
|
234
Godeps/_workspace/src/github.com/docker/docker/pkg/term/winconsole/term_emulator.go
generated
vendored
Normal file
234
Godeps/_workspace/src/github.com/docker/docker/pkg/term/winconsole/term_emulator.go
generated
vendored
Normal file
|
@ -0,0 +1,234 @@
|
|||
package winconsole
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html
|
||||
const (
|
||||
ANSI_ESCAPE_PRIMARY = 0x1B
|
||||
ANSI_ESCAPE_SECONDARY = 0x5B
|
||||
ANSI_COMMAND_FIRST = 0x40
|
||||
ANSI_COMMAND_LAST = 0x7E
|
||||
ANSI_PARAMETER_SEP = ";"
|
||||
ANSI_CMD_G0 = '('
|
||||
ANSI_CMD_G1 = ')'
|
||||
ANSI_CMD_G2 = '*'
|
||||
ANSI_CMD_G3 = '+'
|
||||
ANSI_CMD_DECPNM = '>'
|
||||
ANSI_CMD_DECPAM = '='
|
||||
ANSI_CMD_OSC = ']'
|
||||
ANSI_CMD_STR_TERM = '\\'
|
||||
ANSI_BEL = 0x07
|
||||
KEY_EVENT = 1
|
||||
)
|
||||
|
||||
// Interface that implements terminal handling
|
||||
type terminalEmulator interface {
|
||||
HandleOutputCommand(fd uintptr, command []byte) (n int, err error)
|
||||
HandleInputSequence(fd uintptr, command []byte) (n int, err error)
|
||||
WriteChars(fd uintptr, w io.Writer, p []byte) (n int, err error)
|
||||
ReadChars(fd uintptr, w io.Reader, p []byte) (n int, err error)
|
||||
}
|
||||
|
||||
type terminalWriter struct {
|
||||
wrappedWriter io.Writer
|
||||
emulator terminalEmulator
|
||||
command []byte
|
||||
inSequence bool
|
||||
fd uintptr
|
||||
}
|
||||
|
||||
type terminalReader struct {
|
||||
wrappedReader io.ReadCloser
|
||||
emulator terminalEmulator
|
||||
command []byte
|
||||
inSequence bool
|
||||
fd uintptr
|
||||
}
|
||||
|
||||
// http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html
|
||||
func isAnsiCommandChar(b byte) bool {
|
||||
switch {
|
||||
case ANSI_COMMAND_FIRST <= b && b <= ANSI_COMMAND_LAST && b != ANSI_ESCAPE_SECONDARY:
|
||||
return true
|
||||
case b == ANSI_CMD_G1 || b == ANSI_CMD_OSC || b == ANSI_CMD_DECPAM || b == ANSI_CMD_DECPNM:
|
||||
// non-CSI escape sequence terminator
|
||||
return true
|
||||
case b == ANSI_CMD_STR_TERM || b == ANSI_BEL:
|
||||
// String escape sequence terminator
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isCharacterSelectionCmdChar(b byte) bool {
|
||||
return (b == ANSI_CMD_G0 || b == ANSI_CMD_G1 || b == ANSI_CMD_G2 || b == ANSI_CMD_G3)
|
||||
}
|
||||
|
||||
func isXtermOscSequence(command []byte, current byte) bool {
|
||||
return (len(command) >= 2 && command[0] == ANSI_ESCAPE_PRIMARY && command[1] == ANSI_CMD_OSC && current != ANSI_BEL)
|
||||
}
|
||||
|
||||
// Write writes len(p) bytes from p to the underlying data stream.
|
||||
// http://golang.org/pkg/io/#Writer
|
||||
func (tw *terminalWriter) Write(p []byte) (n int, err error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if tw.emulator == nil {
|
||||
return tw.wrappedWriter.Write(p)
|
||||
}
|
||||
// Emulate terminal by extracting commands and executing them
|
||||
totalWritten := 0
|
||||
start := 0 // indicates start of the next chunk
|
||||
end := len(p)
|
||||
for current := 0; current < end; current++ {
|
||||
if tw.inSequence {
|
||||
// inside escape sequence
|
||||
tw.command = append(tw.command, p[current])
|
||||
if isAnsiCommandChar(p[current]) {
|
||||
if !isXtermOscSequence(tw.command, p[current]) {
|
||||
// found the last command character.
|
||||
// Now we have a complete command.
|
||||
nchar, err := tw.emulator.HandleOutputCommand(tw.fd, tw.command)
|
||||
totalWritten += nchar
|
||||
if err != nil {
|
||||
return totalWritten, err
|
||||
}
|
||||
|
||||
// clear the command
|
||||
// don't include current character again
|
||||
tw.command = tw.command[:0]
|
||||
start = current + 1
|
||||
tw.inSequence = false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if p[current] == ANSI_ESCAPE_PRIMARY {
|
||||
// entering escape sequnce
|
||||
tw.inSequence = true
|
||||
// indicates end of "normal sequence", write whatever you have so far
|
||||
if len(p[start:current]) > 0 {
|
||||
nw, err := tw.emulator.WriteChars(tw.fd, tw.wrappedWriter, p[start:current])
|
||||
totalWritten += nw
|
||||
if err != nil {
|
||||
return totalWritten, err
|
||||
}
|
||||
}
|
||||
// include the current character as part of the next sequence
|
||||
tw.command = append(tw.command, p[current])
|
||||
}
|
||||
}
|
||||
}
|
||||
// note that so far, start of the escape sequence triggers writing out of bytes to console.
|
||||
// For the part _after_ the end of last escape sequence, it is not written out yet. So write it out
|
||||
if !tw.inSequence {
|
||||
// assumption is that we can't be inside sequence and therefore command should be empty
|
||||
if len(p[start:]) > 0 {
|
||||
nw, err := tw.emulator.WriteChars(tw.fd, tw.wrappedWriter, p[start:])
|
||||
totalWritten += nw
|
||||
if err != nil {
|
||||
return totalWritten, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return totalWritten, nil
|
||||
|
||||
}
|
||||
|
||||
// Read reads up to len(p) bytes into p.
|
||||
// http://golang.org/pkg/io/#Reader
|
||||
func (tr *terminalReader) Read(p []byte) (n int, err error) {
|
||||
//Implementations of Read are discouraged from returning a zero byte count
|
||||
// with a nil error, except when len(p) == 0.
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if nil == tr.emulator {
|
||||
return tr.readFromWrappedReader(p)
|
||||
}
|
||||
return tr.emulator.ReadChars(tr.fd, tr.wrappedReader, p)
|
||||
}
|
||||
|
||||
// Close the underlying stream
|
||||
func (tr *terminalReader) Close() (err error) {
|
||||
return tr.wrappedReader.Close()
|
||||
}
|
||||
|
||||
func (tr *terminalReader) readFromWrappedReader(p []byte) (n int, err error) {
|
||||
return tr.wrappedReader.Read(p)
|
||||
}
|
||||
|
||||
type ansiCommand struct {
|
||||
CommandBytes []byte
|
||||
Command string
|
||||
Parameters []string
|
||||
IsSpecial bool
|
||||
}
|
||||
|
||||
func parseAnsiCommand(command []byte) *ansiCommand {
|
||||
if isCharacterSelectionCmdChar(command[1]) {
|
||||
// Is Character Set Selection commands
|
||||
return &ansiCommand{
|
||||
CommandBytes: command,
|
||||
Command: string(command),
|
||||
IsSpecial: true,
|
||||
}
|
||||
}
|
||||
// last char is command character
|
||||
lastCharIndex := len(command) - 1
|
||||
|
||||
retValue := &ansiCommand{
|
||||
CommandBytes: command,
|
||||
Command: string(command[lastCharIndex]),
|
||||
IsSpecial: false,
|
||||
}
|
||||
// more than a single escape
|
||||
if lastCharIndex != 0 {
|
||||
start := 1
|
||||
// skip if double char escape sequence
|
||||
if command[0] == ANSI_ESCAPE_PRIMARY && command[1] == ANSI_ESCAPE_SECONDARY {
|
||||
start++
|
||||
}
|
||||
// convert this to GetNextParam method
|
||||
retValue.Parameters = strings.Split(string(command[start:lastCharIndex]), ANSI_PARAMETER_SEP)
|
||||
}
|
||||
return retValue
|
||||
}
|
||||
|
||||
func (c *ansiCommand) getParam(index int) string {
|
||||
if len(c.Parameters) > index {
|
||||
return c.Parameters[index]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (ac *ansiCommand) String() string {
|
||||
return fmt.Sprintf("0x%v \"%v\" (\"%v\")",
|
||||
bytesToHex(ac.CommandBytes),
|
||||
ac.Command,
|
||||
strings.Join(ac.Parameters, "\",\""))
|
||||
}
|
||||
|
||||
func bytesToHex(b []byte) string {
|
||||
hex := make([]string, len(b))
|
||||
for i, ch := range b {
|
||||
hex[i] = fmt.Sprintf("%X", ch)
|
||||
}
|
||||
return strings.Join(hex, "")
|
||||
}
|
||||
|
||||
func parseInt16OrDefault(s string, defaultValue int16) (n int16, err error) {
|
||||
if s == "" {
|
||||
return defaultValue, nil
|
||||
}
|
||||
parsedValue, err := strconv.ParseInt(s, 10, 16)
|
||||
if err != nil {
|
||||
return defaultValue, err
|
||||
}
|
||||
return int16(parsedValue), nil
|
||||
}
|
388
Godeps/_workspace/src/github.com/docker/docker/pkg/term/winconsole/term_emulator_test.go
generated
vendored
Normal file
388
Godeps/_workspace/src/github.com/docker/docker/pkg/term/winconsole/term_emulator_test.go
generated
vendored
Normal file
|
@ -0,0 +1,388 @@
|
|||
package winconsole
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
WRITE_OPERATION = iota
|
||||
COMMAND_OPERATION = iota
|
||||
)
|
||||
|
||||
var languages = []string{
|
||||
"Български",
|
||||
"Català",
|
||||
"Čeština",
|
||||
"Ελληνικά",
|
||||
"Español",
|
||||
"Esperanto",
|
||||
"Euskara",
|
||||
"Français",
|
||||
"Galego",
|
||||
"한국어",
|
||||
"ქართული",
|
||||
"Latviešu",
|
||||
"Lietuvių",
|
||||
"Magyar",
|
||||
"Nederlands",
|
||||
"日本語",
|
||||
"Norsk bokmål",
|
||||
"Norsk nynorsk",
|
||||
"Polski",
|
||||
"Português",
|
||||
"Română",
|
||||
"Русский",
|
||||
"Slovenčina",
|
||||
"Slovenščina",
|
||||
"Српски",
|
||||
"српскохрватски",
|
||||
"Suomi",
|
||||
"Svenska",
|
||||
"ไทย",
|
||||
"Tiếng Việt",
|
||||
"Türkçe",
|
||||
"Українська",
|
||||
"中文",
|
||||
}
|
||||
|
||||
// Mock terminal handler object
|
||||
type mockTerminal struct {
|
||||
OutputCommandSequence []terminalOperation
|
||||
}
|
||||
|
||||
// Used for recording the callback data
|
||||
type terminalOperation struct {
|
||||
Operation int
|
||||
Data []byte
|
||||
Str string
|
||||
}
|
||||
|
||||
func (mt *mockTerminal) record(operation int, data []byte) {
|
||||
op := terminalOperation{
|
||||
Operation: operation,
|
||||
Data: make([]byte, len(data)),
|
||||
}
|
||||
copy(op.Data, data)
|
||||
op.Str = string(op.Data)
|
||||
mt.OutputCommandSequence = append(mt.OutputCommandSequence, op)
|
||||
}
|
||||
|
||||
func (mt *mockTerminal) HandleOutputCommand(fd uintptr, command []byte) (n int, err error) {
|
||||
mt.record(COMMAND_OPERATION, command)
|
||||
return len(command), nil
|
||||
}
|
||||
|
||||
func (mt *mockTerminal) HandleInputSequence(fd uintptr, command []byte) (n int, err error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (mt *mockTerminal) WriteChars(fd uintptr, w io.Writer, p []byte) (n int, err error) {
|
||||
mt.record(WRITE_OPERATION, p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (mt *mockTerminal) ReadChars(fd uintptr, w io.Reader, p []byte) (n int, err error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func assertTrue(t *testing.T, cond bool, format string, args ...interface{}) {
|
||||
if !cond {
|
||||
t.Errorf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// reflect.DeepEqual does not provide detailed information as to what excatly failed.
|
||||
func assertBytesEqual(t *testing.T, expected, actual []byte, format string, args ...interface{}) {
|
||||
match := true
|
||||
mismatchIndex := 0
|
||||
if len(expected) == len(actual) {
|
||||
for i := 0; i < len(expected); i++ {
|
||||
if expected[i] != actual[i] {
|
||||
match = false
|
||||
mismatchIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match = false
|
||||
t.Errorf("Lengths don't match Expected=%d Actual=%d", len(expected), len(actual))
|
||||
}
|
||||
if !match {
|
||||
t.Errorf("Mismatch at index %d ", mismatchIndex)
|
||||
t.Errorf("\tActual String = %s", string(actual))
|
||||
t.Errorf("\tExpected String = %s", string(expected))
|
||||
t.Errorf("\tActual = %v", actual)
|
||||
t.Errorf("\tExpected = %v", expected)
|
||||
t.Errorf(format, args)
|
||||
}
|
||||
}
|
||||
|
||||
// Just to make sure :)
|
||||
func TestAssertEqualBytes(t *testing.T) {
|
||||
data := []byte{9, 9, 1, 1, 1, 9, 9}
|
||||
assertBytesEqual(t, data, data, "Self")
|
||||
assertBytesEqual(t, data[1:4], data[1:4], "Self")
|
||||
assertBytesEqual(t, []byte{1, 1}, []byte{1, 1}, "Simple match")
|
||||
assertBytesEqual(t, []byte{1, 2, 3}, []byte{1, 2, 3}, "content mismatch")
|
||||
assertBytesEqual(t, []byte{1, 1, 1}, data[2:5], "slice match")
|
||||
}
|
||||
|
||||
/*
|
||||
func TestAssertEqualBytesNegative(t *testing.T) {
|
||||
AssertBytesEqual(t, []byte{1, 1}, []byte{1}, "Length mismatch")
|
||||
AssertBytesEqual(t, []byte{1, 1}, []byte{1}, "Length mismatch")
|
||||
AssertBytesEqual(t, []byte{1, 2, 3}, []byte{1, 1, 1}, "content mismatch")
|
||||
}*/
|
||||
|
||||
// Checks that the calls received
|
||||
func assertHandlerOutput(t *testing.T, mock *mockTerminal, plainText string, commands ...string) {
|
||||
text := make([]byte, 0, 3*len(plainText))
|
||||
cmdIndex := 0
|
||||
for opIndex := 0; opIndex < len(mock.OutputCommandSequence); opIndex++ {
|
||||
op := mock.OutputCommandSequence[opIndex]
|
||||
if op.Operation == WRITE_OPERATION {
|
||||
t.Logf("\nThe data is[%d] == %s", opIndex, string(op.Data))
|
||||
text = append(text[:], op.Data...)
|
||||
} else {
|
||||
assertTrue(t, mock.OutputCommandSequence[opIndex].Operation == COMMAND_OPERATION, "Operation should be command : %s", fmt.Sprintf("%+v", mock))
|
||||
assertBytesEqual(t, StringToBytes(commands[cmdIndex]), mock.OutputCommandSequence[opIndex].Data, "Command data should match")
|
||||
cmdIndex++
|
||||
}
|
||||
}
|
||||
assertBytesEqual(t, StringToBytes(plainText), text, "Command data should match %#v", mock)
|
||||
}
|
||||
|
||||
func StringToBytes(str string) []byte {
|
||||
bytes := make([]byte, len(str))
|
||||
copy(bytes[:], str)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func TestParseAnsiCommand(t *testing.T) {
|
||||
// Note: if the parameter does not exist then the empty value is returned
|
||||
|
||||
c := parseAnsiCommand(StringToBytes("\x1Bm"))
|
||||
assertTrue(t, c.Command == "m", "Command should be m")
|
||||
assertTrue(t, "" == c.getParam(0), "should return empty string")
|
||||
assertTrue(t, "" == c.getParam(1), "should return empty string")
|
||||
|
||||
// Escape sequence - ESC[
|
||||
c = parseAnsiCommand(StringToBytes("\x1B[m"))
|
||||
assertTrue(t, c.Command == "m", "Command should be m")
|
||||
assertTrue(t, "" == c.getParam(0), "should return empty string")
|
||||
assertTrue(t, "" == c.getParam(1), "should return empty string")
|
||||
|
||||
// Escape sequence With empty parameters- ESC[
|
||||
c = parseAnsiCommand(StringToBytes("\x1B[;m"))
|
||||
assertTrue(t, c.Command == "m", "Command should be m")
|
||||
assertTrue(t, "" == c.getParam(0), "should return empty string")
|
||||
assertTrue(t, "" == c.getParam(1), "should return empty string")
|
||||
assertTrue(t, "" == c.getParam(2), "should return empty string")
|
||||
|
||||
// Escape sequence With empty muliple parameters- ESC[
|
||||
c = parseAnsiCommand(StringToBytes("\x1B[;;m"))
|
||||
assertTrue(t, c.Command == "m", "Command should be m")
|
||||
assertTrue(t, "" == c.getParam(0), "")
|
||||
assertTrue(t, "" == c.getParam(1), "")
|
||||
assertTrue(t, "" == c.getParam(2), "")
|
||||
|
||||
// Escape sequence With muliple parameters- ESC[
|
||||
c = parseAnsiCommand(StringToBytes("\x1B[1;2;3m"))
|
||||
assertTrue(t, c.Command == "m", "Command should be m")
|
||||
assertTrue(t, "1" == c.getParam(0), "")
|
||||
assertTrue(t, "2" == c.getParam(1), "")
|
||||
assertTrue(t, "3" == c.getParam(2), "")
|
||||
|
||||
// Escape sequence With muliple parameters- some missing
|
||||
c = parseAnsiCommand(StringToBytes("\x1B[1;;3;;;6m"))
|
||||
assertTrue(t, c.Command == "m", "Command should be m")
|
||||
assertTrue(t, "1" == c.getParam(0), "")
|
||||
assertTrue(t, "" == c.getParam(1), "")
|
||||
assertTrue(t, "3" == c.getParam(2), "")
|
||||
assertTrue(t, "" == c.getParam(3), "")
|
||||
assertTrue(t, "" == c.getParam(4), "")
|
||||
assertTrue(t, "6" == c.getParam(5), "")
|
||||
}
|
||||
|
||||
func newBufferedMockTerm() (stdOut io.Writer, stdErr io.Writer, stdIn io.ReadCloser, mock *mockTerminal) {
|
||||
var input bytes.Buffer
|
||||
var output bytes.Buffer
|
||||
var err bytes.Buffer
|
||||
|
||||
mock = &mockTerminal{
|
||||
OutputCommandSequence: make([]terminalOperation, 0, 256),
|
||||
}
|
||||
|
||||
stdOut = &terminalWriter{
|
||||
wrappedWriter: &output,
|
||||
emulator: mock,
|
||||
command: make([]byte, 0, 256),
|
||||
}
|
||||
stdErr = &terminalWriter{
|
||||
wrappedWriter: &err,
|
||||
emulator: mock,
|
||||
command: make([]byte, 0, 256),
|
||||
}
|
||||
stdIn = &terminalReader{
|
||||
wrappedReader: ioutil.NopCloser(&input),
|
||||
emulator: mock,
|
||||
command: make([]byte, 0, 256),
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func TestOutputSimple(t *testing.T) {
|
||||
stdOut, _, _, mock := newBufferedMockTerm()
|
||||
|
||||
stdOut.Write(StringToBytes("Hello world"))
|
||||
stdOut.Write(StringToBytes("\x1BmHello again"))
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
|
||||
assertBytesEqual(t, StringToBytes("Hello world"), mock.OutputCommandSequence[0].Data, "Write data should match")
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[1].Operation == COMMAND_OPERATION, "Operation should be command : %+v", mock)
|
||||
assertBytesEqual(t, StringToBytes("\x1Bm"), mock.OutputCommandSequence[1].Data, "Command data should match")
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[2].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
|
||||
assertBytesEqual(t, StringToBytes("Hello again"), mock.OutputCommandSequence[2].Data, "Write data should match")
|
||||
}
|
||||
|
||||
func TestOutputSplitCommand(t *testing.T) {
|
||||
stdOut, _, _, mock := newBufferedMockTerm()
|
||||
|
||||
stdOut.Write(StringToBytes("Hello world\x1B[1;2;3"))
|
||||
stdOut.Write(StringToBytes("mHello again"))
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
|
||||
assertBytesEqual(t, StringToBytes("Hello world"), mock.OutputCommandSequence[0].Data, "Write data should match")
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[1].Operation == COMMAND_OPERATION, "Operation should be command : %+v", mock)
|
||||
assertBytesEqual(t, StringToBytes("\x1B[1;2;3m"), mock.OutputCommandSequence[1].Data, "Command data should match")
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[2].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
|
||||
assertBytesEqual(t, StringToBytes("Hello again"), mock.OutputCommandSequence[2].Data, "Write data should match")
|
||||
}
|
||||
|
||||
func TestOutputMultipleCommands(t *testing.T) {
|
||||
stdOut, _, _, mock := newBufferedMockTerm()
|
||||
|
||||
stdOut.Write(StringToBytes("Hello world"))
|
||||
stdOut.Write(StringToBytes("\x1B[1;2;3m"))
|
||||
stdOut.Write(StringToBytes("\x1B[J"))
|
||||
stdOut.Write(StringToBytes("Hello again"))
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
|
||||
assertBytesEqual(t, StringToBytes("Hello world"), mock.OutputCommandSequence[0].Data, "Write data should match")
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[1].Operation == COMMAND_OPERATION, "Operation should be command : %+v", mock)
|
||||
assertBytesEqual(t, StringToBytes("\x1B[1;2;3m"), mock.OutputCommandSequence[1].Data, "Command data should match")
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[2].Operation == COMMAND_OPERATION, "Operation should be command : %+v", mock)
|
||||
assertBytesEqual(t, StringToBytes("\x1B[J"), mock.OutputCommandSequence[2].Data, "Command data should match")
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[3].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
|
||||
assertBytesEqual(t, StringToBytes("Hello again"), mock.OutputCommandSequence[3].Data, "Write data should match")
|
||||
}
|
||||
|
||||
// Splits the given data in two chunks , makes two writes and checks the split data is parsed correctly
|
||||
// checks output write/command is passed to handler correctly
|
||||
func helpsTestOutputSplitChunksAtIndex(t *testing.T, i int, data []byte) {
|
||||
t.Logf("\ni=%d", i)
|
||||
stdOut, _, _, mock := newBufferedMockTerm()
|
||||
|
||||
t.Logf("\nWriting chunk[0] == %s", string(data[:i]))
|
||||
t.Logf("\nWriting chunk[1] == %s", string(data[i:]))
|
||||
stdOut.Write(data[:i])
|
||||
stdOut.Write(data[i:])
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
|
||||
assertBytesEqual(t, data[:i], mock.OutputCommandSequence[0].Data, "Write data should match")
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[1].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
|
||||
assertBytesEqual(t, data[i:], mock.OutputCommandSequence[1].Data, "Write data should match")
|
||||
}
|
||||
|
||||
// Splits the given data in three chunks , makes three writes and checks the split data is parsed correctly
|
||||
// checks output write/command is passed to handler correctly
|
||||
func helpsTestOutputSplitThreeChunksAtIndex(t *testing.T, data []byte, i int, j int) {
|
||||
stdOut, _, _, mock := newBufferedMockTerm()
|
||||
|
||||
t.Logf("\nWriting chunk[0] == %s", string(data[:i]))
|
||||
t.Logf("\nWriting chunk[1] == %s", string(data[i:j]))
|
||||
t.Logf("\nWriting chunk[2] == %s", string(data[j:]))
|
||||
stdOut.Write(data[:i])
|
||||
stdOut.Write(data[i:j])
|
||||
stdOut.Write(data[j:])
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
|
||||
assertBytesEqual(t, data[:i], mock.OutputCommandSequence[0].Data, "Write data should match")
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[1].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
|
||||
assertBytesEqual(t, data[i:j], mock.OutputCommandSequence[1].Data, "Write data should match")
|
||||
|
||||
assertTrue(t, mock.OutputCommandSequence[2].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock)
|
||||
assertBytesEqual(t, data[j:], mock.OutputCommandSequence[2].Data, "Write data should match")
|
||||
}
|
||||
|
||||
// Splits the output into two parts and tests all such possible pairs
|
||||
func helpsTestOutputSplitChunks(t *testing.T, data []byte) {
|
||||
for i := 1; i < len(data)-1; i++ {
|
||||
helpsTestOutputSplitChunksAtIndex(t, i, data)
|
||||
}
|
||||
}
|
||||
|
||||
// Splits the output in three parts and tests all such possible triples
|
||||
func helpsTestOutputSplitThreeChunks(t *testing.T, data []byte) {
|
||||
for i := 1; i < len(data)-2; i++ {
|
||||
for j := i + 1; j < len(data)-1; j++ {
|
||||
helpsTestOutputSplitThreeChunksAtIndex(t, data, i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func helpsTestOutputSplitCommandsAtIndex(t *testing.T, data []byte, i int, plainText string, commands ...string) {
|
||||
t.Logf("\ni=%d", i)
|
||||
stdOut, _, _, mock := newBufferedMockTerm()
|
||||
|
||||
stdOut.Write(data[:i])
|
||||
stdOut.Write(data[i:])
|
||||
assertHandlerOutput(t, mock, plainText, commands...)
|
||||
}
|
||||
|
||||
func helpsTestOutputSplitCommands(t *testing.T, data []byte, plainText string, commands ...string) {
|
||||
for i := 1; i < len(data)-1; i++ {
|
||||
helpsTestOutputSplitCommandsAtIndex(t, data, i, plainText, commands...)
|
||||
}
|
||||
}
|
||||
|
||||
func injectCommandAt(data string, i int, command string) string {
|
||||
retValue := make([]byte, len(data)+len(command)+4)
|
||||
retValue = append(retValue, data[:i]...)
|
||||
retValue = append(retValue, data[i:]...)
|
||||
return string(retValue)
|
||||
}
|
||||
|
||||
func TestOutputSplitChunks(t *testing.T) {
|
||||
data := StringToBytes("qwertyuiopasdfghjklzxcvbnm")
|
||||
helpsTestOutputSplitChunks(t, data)
|
||||
helpsTestOutputSplitChunks(t, StringToBytes("BBBBB"))
|
||||
helpsTestOutputSplitThreeChunks(t, StringToBytes("ABCDE"))
|
||||
}
|
||||
|
||||
func TestOutputSplitChunksIncludingCommands(t *testing.T) {
|
||||
helpsTestOutputSplitCommands(t, StringToBytes("Hello world.\x1B[mHello again."), "Hello world.Hello again.", "\x1B[m")
|
||||
helpsTestOutputSplitCommandsAtIndex(t, StringToBytes("Hello world.\x1B[mHello again."), 2, "Hello world.Hello again.", "\x1B[m")
|
||||
}
|
||||
|
||||
func TestSplitChunkUnicode(t *testing.T) {
|
||||
for _, l := range languages {
|
||||
data := StringToBytes(l)
|
||||
helpsTestOutputSplitChunks(t, data)
|
||||
helpsTestOutputSplitThreeChunks(t, data)
|
||||
}
|
||||
}
|
47
Godeps/_workspace/src/github.com/docker/docker/pkg/timeutils/json_test.go
generated
vendored
Normal file
47
Godeps/_workspace/src/github.com/docker/docker/pkg/timeutils/json_test.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
package timeutils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Testing to ensure 'year' fields is between 0 and 9999
|
||||
func TestFastMarshalJSONWithInvalidDate(t *testing.T) {
|
||||
aTime := time.Date(-1, 1, 1, 0, 0, 0, 0, time.Local)
|
||||
json, err := FastMarshalJSON(aTime)
|
||||
if err == nil {
|
||||
t.Fatalf("FastMarshalJSON should throw an error, but was '%v'", json)
|
||||
}
|
||||
anotherTime := time.Date(10000, 1, 1, 0, 0, 0, 0, time.Local)
|
||||
json, err = FastMarshalJSON(anotherTime)
|
||||
if err == nil {
|
||||
t.Fatalf("FastMarshalJSON should throw an error, but was '%v'", json)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFastMarshalJSON(t *testing.T) {
|
||||
aTime := time.Date(2015, 5, 29, 11, 1, 2, 3, time.UTC)
|
||||
json, err := FastMarshalJSON(aTime)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := "\"2015-05-29T11:01:02.000000003Z\""
|
||||
if json != expected {
|
||||
t.Fatalf("Expected %v, got %v", expected, json)
|
||||
}
|
||||
|
||||
location, err := time.LoadLocation("Europe/Paris")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
aTime = time.Date(2015, 5, 29, 11, 1, 2, 3, location)
|
||||
json, err = FastMarshalJSON(aTime)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected = "\"2015-05-29T11:01:02.000000003+02:00\""
|
||||
if json != expected {
|
||||
t.Fatalf("Expected %v, got %v", expected, json)
|
||||
}
|
||||
}
|
|
@ -6,10 +6,17 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// GetTimestamp tries to parse given string as RFC3339 time
|
||||
// or Unix timestamp (with seconds precision), if successful
|
||||
//returns a Unix timestamp as string otherwise returns value back.
|
||||
func GetTimestamp(value string) string {
|
||||
// GetTimestamp tries to parse given string as golang duration,
|
||||
// then RFC3339 time and finally as a Unix timestamp. If
|
||||
// any of these were successful, it returns a Unix timestamp
|
||||
// as string otherwise returns the given value back.
|
||||
// In case of duration input, the returned timestamp is computed
|
||||
// as the given reference time minus the amount of the duration.
|
||||
func GetTimestamp(value string, reference time.Time) string {
|
||||
if d, err := time.ParseDuration(value); value != "0" && err == nil {
|
||||
return strconv.FormatInt(reference.Add(-d).Unix(), 10)
|
||||
}
|
||||
|
||||
var format string
|
||||
if strings.Contains(value, ".") {
|
||||
format = time.RFC3339Nano
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
package timeutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGetTimestamp(t *testing.T) {
|
||||
now := time.Now()
|
||||
cases := []struct{ in, expected string }{
|
||||
{"0", "-62167305600"}, // 0 gets parsed year 0
|
||||
|
||||
|
@ -23,12 +26,17 @@ func TestGetTimestamp(t *testing.T) {
|
|||
// unix timestamps returned as is
|
||||
{"1136073600", "1136073600"},
|
||||
|
||||
// Durations
|
||||
{"1m", fmt.Sprintf("%d", now.Add(-1*time.Minute).Unix())},
|
||||
{"1.5h", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix())},
|
||||
{"1h30m", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix())},
|
||||
|
||||
// String fallback
|
||||
{"invalid", "invalid"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
o := GetTimestamp(c.in)
|
||||
o := GetTimestamp(c.in, now)
|
||||
if o != c.expected {
|
||||
t.Fatalf("wrong value for '%s'. expected:'%s' got:'%s'", c.in, c.expected, o)
|
||||
}
|
||||
|
|
|
@ -27,5 +27,5 @@ func HumanDuration(d time.Duration) string {
|
|||
} else if hours < 24*365*2 {
|
||||
return fmt.Sprintf("%d months", hours/24/30)
|
||||
}
|
||||
return fmt.Sprintf("%f years", d.Hours()/24/365)
|
||||
return fmt.Sprintf("%d years", int(d.Hours())/24/365)
|
||||
}
|
||||
|
|
|
@ -41,6 +41,6 @@ func TestHumanDuration(t *testing.T) {
|
|||
assertEquals(t, "13 months", HumanDuration(13*month))
|
||||
assertEquals(t, "23 months", HumanDuration(23*month))
|
||||
assertEquals(t, "24 months", HumanDuration(24*month))
|
||||
assertEquals(t, "2.010959 years", HumanDuration(24*month+2*week))
|
||||
assertEquals(t, "3.164384 years", HumanDuration(3*year+2*month))
|
||||
assertEquals(t, "2 years", HumanDuration(24*month+2*week))
|
||||
assertEquals(t, "3 years", HumanDuration(3*year+2*month))
|
||||
}
|
||||
|
|
|
@ -37,23 +37,25 @@ var (
|
|||
var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
|
||||
var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
|
||||
|
||||
// CustomSize returns a human-readable approximation of a size
|
||||
// using custom format
|
||||
func CustomSize(format string, size float64, base float64, _map []string) string {
|
||||
i := 0
|
||||
for size >= base {
|
||||
size = size / base
|
||||
i++
|
||||
}
|
||||
return fmt.Sprintf(format, size, _map[i])
|
||||
}
|
||||
|
||||
// HumanSize returns a human-readable approximation of a size
|
||||
// using SI standard (eg. "44kB", "17MB")
|
||||
func HumanSize(size float64) string {
|
||||
return intToString(float64(size), 1000.0, decimapAbbrs)
|
||||
return CustomSize("%.4g %s", float64(size), 1000.0, decimapAbbrs)
|
||||
}
|
||||
|
||||
func BytesSize(size float64) string {
|
||||
return intToString(size, 1024.0, binaryAbbrs)
|
||||
}
|
||||
|
||||
func intToString(size, unit float64, _map []string) string {
|
||||
i := 0
|
||||
for size >= unit {
|
||||
size = size / unit
|
||||
i++
|
||||
}
|
||||
return fmt.Sprintf("%.4g %s", size, _map[i])
|
||||
return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs)
|
||||
}
|
||||
|
||||
// FromHumanSize returns an integer from a human-readable specification of a
|
||||
|
|
Loading…
Reference in New Issue