Lint on pkg/* packages

- pkg/useragent
- pkg/units
- pkg/ulimit
- pkg/truncindex
- pkg/timeoutconn
- pkg/term
- pkg/tarsum
- pkg/tailfile
- pkg/systemd
- pkg/stringutils
- pkg/stringid
- pkg/streamformatter
- pkg/sockets
- pkg/signal
- pkg/proxy
- pkg/progressreader
- pkg/pools
- pkg/plugins
- pkg/pidfile
- pkg/parsers
- pkg/parsers/filters
- pkg/parsers/kernel
- pkg/parsers/operatingsystem

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
Vincent Demeester 2015-07-25 10:35:07 +02:00
parent a922d62168
commit 9bcb3cba83
56 changed files with 455 additions and 195 deletions

View file

@ -1,3 +1,5 @@
// Package filters provides helper function to parse and handle command line
// filter, used for example in docker ps or docker images commands.
package filters package filters
import ( import (
@ -7,16 +9,22 @@ import (
"strings" "strings"
) )
// Args stores filter arguments as map key:{array of values}.
// It contains a aggregation of the list of arguments (which are in the form
// of -f 'key=value') based on the key, and store values for the same key
// in an slice.
// e.g given -f 'label=label1=1' -f 'label=label2=2' -f 'image.name=ubuntu'
// the args will be {'label': {'label1=1','label2=2'}, 'image.name', {'ubuntu'}}
type Args map[string][]string type Args map[string][]string
// Parse the argument to the filter flag. Like // ParseFlag parses the argument to the filter flag. Like
// //
// `docker ps -f 'created=today' -f 'image.name=ubuntu*'` // `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 // If prev map is provided, then it is appended to, and returned. By default a new
// map is created. // map is created.
func ParseFlag(arg string, prev Args) (Args, error) { func ParseFlag(arg string, prev Args) (Args, error) {
var filters Args = prev filters := prev
if prev == nil { if prev == nil {
filters = Args{} filters = Args{}
} }
@ -25,7 +33,7 @@ func ParseFlag(arg string, prev Args) (Args, error) {
} }
if !strings.Contains(arg, "=") { if !strings.Contains(arg, "=") {
return filters, ErrorBadFormat return filters, ErrBadFormat
} }
f := strings.SplitN(arg, "=", 2) f := strings.SplitN(arg, "=", 2)
@ -36,9 +44,10 @@ func ParseFlag(arg string, prev Args) (Args, error) {
return filters, nil return filters, nil
} }
var ErrorBadFormat = errors.New("bad format of filter (expected name=value)") // ErrBadFormat is an error returned in case of bad format for a filter.
var ErrBadFormat = errors.New("bad format of filter (expected name=value)")
// packs the Args into an string for easy transport from client to server // ToParam packs the Args into an string for easy transport from client to server.
func ToParam(a Args) (string, error) { func ToParam(a Args) (string, error) {
// this way we don't URL encode {}, just empty space // this way we don't URL encode {}, just empty space
if len(a) == 0 { if len(a) == 0 {
@ -52,7 +61,7 @@ func ToParam(a Args) (string, error) {
return string(buf), nil return string(buf), nil
} }
// unpacks the filter Args // FromParam unpacks the filter Args.
func FromParam(p string) (Args, error) { func FromParam(p string) (Args, error) {
args := Args{} args := Args{}
if len(p) == 0 { if len(p) == 0 {
@ -64,6 +73,11 @@ func FromParam(p string) (Args, error) {
return args, nil return args, nil
} }
// MatchKVList returns true if the values for the specified field maches the ones
// from the sources.
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
// field is 'label' and sources are {'label':{'label1=1','label2=2','label3=3'}}
// it returns true.
func (filters Args) MatchKVList(field string, sources map[string]string) bool { func (filters Args) MatchKVList(field string, sources map[string]string) bool {
fieldValues := filters[field] fieldValues := filters[field]
@ -96,6 +110,10 @@ outer:
return true return true
} }
// Match returns true if the values for the specified field matches the source string
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
// field is 'image.name' and source is 'ubuntu'
// it returns true.
func (filters Args) Match(field, source string) bool { func (filters Args) Match(field, source string) bool {
fieldValues := filters[field] fieldValues := filters[field]

View file

@ -39,8 +39,8 @@ func TestParseArgsEdgeCase(t *testing.T) {
if args == nil || len(args) != 0 { if args == nil || len(args) != 0 {
t.Fatalf("Expected an empty Args (map), got %v", args) t.Fatalf("Expected an empty Args (map), got %v", args)
} }
if args, err = ParseFlag("anything", args); err == nil || err != ErrorBadFormat { if args, err = ParseFlag("anything", args); err == nil || err != ErrBadFormat {
t.Fatalf("Expected ErrorBadFormat, got %v", err) t.Fatalf("Expected ErrBadFormat, got %v", err)
} }
} }

View file

@ -1,5 +1,7 @@
// +build !windows // +build !windows
// Package kernel provides helper function to get, parse and compare kernel
// versions for different platforms.
package kernel package kernel
import ( import (
@ -8,20 +10,21 @@ import (
"fmt" "fmt"
) )
type KernelVersionInfo struct { // VersionInfo holds information about the kernel.
Kernel int type VersionInfo struct {
Major int Kernel int // Version of the kernel (e.g. 4.1.2-generic -> 4)
Minor int Major int // Major part of the kernel version (e.g. 4.1.2-generic -> 1)
Flavor string Minor int // Minor part of the kernel version (e.g. 4.1.2-generic -> 2)
Flavor string // Flavor of the kernel version (e.g. 4.1.2-generic -> generic)
} }
func (k *KernelVersionInfo) String() string { func (k *VersionInfo) String() string {
return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, k.Flavor) return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, k.Flavor)
} }
// Compare two KernelVersionInfo struct. // CompareKernelVersion compares two kernel.VersionInfo structs.
// Returns -1 if a < b, 0 if a == b, 1 it a > b // Returns -1 if a < b, 0 if a == b, 1 it a > b
func CompareKernelVersion(a, b *KernelVersionInfo) int { func CompareKernelVersion(a, b VersionInfo) int {
if a.Kernel < b.Kernel { if a.Kernel < b.Kernel {
return -1 return -1
} else if a.Kernel > b.Kernel { } else if a.Kernel > b.Kernel {
@ -43,7 +46,8 @@ func CompareKernelVersion(a, b *KernelVersionInfo) int {
return 0 return 0
} }
func GetKernelVersion() (*KernelVersionInfo, error) { // GetKernelVersion gets the current kernel version.
func GetKernelVersion() (*VersionInfo, error) {
var ( var (
err error err error
) )
@ -67,7 +71,8 @@ func GetKernelVersion() (*KernelVersionInfo, error) {
return ParseRelease(string(release)) return ParseRelease(string(release))
} }
func ParseRelease(release string) (*KernelVersionInfo, error) { // ParseRelease parses a string and creates a VersionInfo based on it.
func ParseRelease(release string) (*VersionInfo, error) {
var ( var (
kernel, major, minor, parsed int kernel, major, minor, parsed int
flavor, partial string flavor, partial string
@ -86,7 +91,7 @@ func ParseRelease(release string) (*KernelVersionInfo, error) {
flavor = partial flavor = partial
} }
return &KernelVersionInfo{ return &VersionInfo{
Kernel: kernel, Kernel: kernel,
Major: major, Major: major,
Minor: minor, Minor: minor,

View file

@ -5,13 +5,13 @@ import (
"testing" "testing"
) )
func assertParseRelease(t *testing.T, release string, b *KernelVersionInfo, result int) { func assertParseRelease(t *testing.T, release string, b *VersionInfo, result int) {
var ( var (
a *KernelVersionInfo a *VersionInfo
) )
a, _ = ParseRelease(release) a, _ = ParseRelease(release)
if r := CompareKernelVersion(a, b); r != result { if r := CompareKernelVersion(*a, *b); r != result {
t.Fatalf("Unexpected kernel version comparison result for (%v,%v). Found %d, expected %d", release, b, r, result) t.Fatalf("Unexpected kernel version comparison result for (%v,%v). Found %d, expected %d", release, b, r, result)
} }
if a.Flavor != b.Flavor { if a.Flavor != b.Flavor {
@ -20,13 +20,13 @@ func assertParseRelease(t *testing.T, release string, b *KernelVersionInfo, resu
} }
func TestParseRelease(t *testing.T) { func TestParseRelease(t *testing.T) {
assertParseRelease(t, "3.8.0", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, 0) assertParseRelease(t, "3.8.0", &VersionInfo{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", &VersionInfo{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.4.54.longterm-1", &VersionInfo{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.8.0-19-generic", &VersionInfo{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.8tag", &VersionInfo{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) assertParseRelease(t, "3.12-1-amd64", &VersionInfo{Kernel: 3, Major: 12, Minor: 0, Flavor: "-1-amd64"}, 0)
assertParseRelease(t, "3.8.0", &KernelVersionInfo{Kernel: 4, Major: 8, Minor: 0}, -1) assertParseRelease(t, "3.8.0", &VersionInfo{Kernel: 4, Major: 8, Minor: 0}, -1)
// Errors // Errors
invalids := []string{ invalids := []string{
"3", "3",
@ -42,7 +42,7 @@ func TestParseRelease(t *testing.T) {
} }
} }
func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) { func assertKernelVersion(t *testing.T, a, b VersionInfo, result int) {
if r := CompareKernelVersion(a, b); r != result { if r := CompareKernelVersion(a, b); r != result {
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result) t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
} }
@ -50,43 +50,43 @@ func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) {
func TestCompareKernelVersion(t *testing.T) { func TestCompareKernelVersion(t *testing.T) {
assertKernelVersion(t, assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, VersionInfo{Kernel: 3, Major: 8, Minor: 0},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, VersionInfo{Kernel: 3, Major: 8, Minor: 0},
0) 0)
assertKernelVersion(t, assertKernelVersion(t,
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0}, VersionInfo{Kernel: 2, Major: 6, Minor: 0},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, VersionInfo{Kernel: 3, Major: 8, Minor: 0},
-1) -1)
assertKernelVersion(t, assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, VersionInfo{Kernel: 3, Major: 8, Minor: 0},
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0}, VersionInfo{Kernel: 2, Major: 6, Minor: 0},
1) 1)
assertKernelVersion(t, assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, VersionInfo{Kernel: 3, Major: 8, Minor: 0},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, VersionInfo{Kernel: 3, Major: 8, Minor: 0},
0) 0)
assertKernelVersion(t, assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5}, VersionInfo{Kernel: 3, Major: 8, Minor: 5},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, VersionInfo{Kernel: 3, Major: 8, Minor: 0},
1) 1)
assertKernelVersion(t, assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20}, VersionInfo{Kernel: 3, Major: 0, Minor: 20},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, VersionInfo{Kernel: 3, Major: 8, Minor: 0},
-1) -1)
assertKernelVersion(t, assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 7, Minor: 20}, VersionInfo{Kernel: 3, Major: 7, Minor: 20},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, VersionInfo{Kernel: 3, Major: 8, Minor: 0},
-1) -1)
assertKernelVersion(t, assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 20}, VersionInfo{Kernel: 3, Major: 8, Minor: 20},
&KernelVersionInfo{Kernel: 3, Major: 7, Minor: 0}, VersionInfo{Kernel: 3, Major: 7, Minor: 0},
1) 1)
assertKernelVersion(t, assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 20}, VersionInfo{Kernel: 3, Major: 8, Minor: 20},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, VersionInfo{Kernel: 3, Major: 8, Minor: 0},
1) 1)
assertKernelVersion(t, assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, VersionInfo{Kernel: 3, Major: 8, Minor: 0},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 20}, VersionInfo{Kernel: 3, Major: 8, Minor: 20},
-1) -1)
} }

View file

@ -6,18 +6,20 @@ import (
"unsafe" "unsafe"
) )
type KernelVersionInfo struct { // VersionInfo holds information about the kernel.
kvi string type VersionInfo struct {
major int kvi string // Version of the kernel (e.g. 6.1.7601.17592 -> 6)
minor int major int // Major part of the kernel version (e.g. 6.1.7601.17592 -> 1)
build int minor int // Minor part of the kernel version (e.g. 6.1.7601.17592 -> 7601)
build int // Build number of the kernel version (e.g. 6.1.7601.17592 -> 17592)
} }
func (k *KernelVersionInfo) String() string { func (k *VersionInfo) String() string {
return fmt.Sprintf("%d.%d %d (%s)", k.major, k.minor, k.build, k.kvi) return fmt.Sprintf("%d.%d %d (%s)", k.major, k.minor, k.build, k.kvi)
} }
func GetKernelVersion() (*KernelVersionInfo, error) { // GetKernelVersion gets the current kernel version.
func GetKernelVersion() (*VersionInfo, error) {
var ( var (
h syscall.Handle h syscall.Handle
@ -25,7 +27,7 @@ func GetKernelVersion() (*KernelVersionInfo, error) {
err error err error
) )
KVI := &KernelVersionInfo{"Unknown", 0, 0, 0} KVI := &VersionInfo{"Unknown", 0, 0, 0}
if err = syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, if err = syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE,
syscall.StringToUTF16Ptr(`SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\`), syscall.StringToUTF16Ptr(`SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\`),

View file

@ -4,6 +4,9 @@ import (
"syscall" "syscall"
) )
// Utsname represents the system name structure.
// It is passthgrouh for syscall.Utsname in order to make it portable with
// other platforms where it is not available.
type Utsname syscall.Utsname type Utsname syscall.Utsname
func uname() (*syscall.Utsname, error) { func uname() (*syscall.Utsname, error) {

View file

@ -6,6 +6,9 @@ import (
"errors" "errors"
) )
// Utsname represents the system name structure.
// It is defined here to make it portable as it is available on linux but not
// on windows.
type Utsname struct { type Utsname struct {
Release [65]byte Release [65]byte
} }

View file

@ -1,3 +1,5 @@
// Package operatingsystem provides helper function to get the operating system
// name for different platforms.
package operatingsystem package operatingsystem
import ( import (
@ -14,6 +16,7 @@ var (
etcOsRelease = "/etc/os-release" etcOsRelease = "/etc/os-release"
) )
// GetOperatingSystem gets the name of the current operating system.
func GetOperatingSystem() (string, error) { func GetOperatingSystem() (string, error) {
b, err := ioutil.ReadFile(etcOsRelease) b, err := ioutil.ReadFile(etcOsRelease)
if err != nil { if err != nil {
@ -26,6 +29,7 @@ func GetOperatingSystem() (string, error) {
return "", errors.New("PRETTY_NAME not found") return "", errors.New("PRETTY_NAME not found")
} }
// IsContainerized returns true if we are running inside a container.
func IsContainerized() (bool, error) { func IsContainerized() (bool, error) {
b, err := ioutil.ReadFile(proc1Cgroup) b, err := ioutil.ReadFile(proc1Cgroup)
if err != nil { if err != nil {

View file

@ -8,6 +8,7 @@ import (
// See https://code.google.com/p/go/source/browse/src/pkg/mime/type_windows.go?r=d14520ac25bf6940785aabb71f5be453a286f58c // See https://code.google.com/p/go/source/browse/src/pkg/mime/type_windows.go?r=d14520ac25bf6940785aabb71f5be453a286f58c
// for a similar sample // for a similar sample
// GetOperatingSystem gets the name of the current operating system.
func GetOperatingSystem() (string, error) { func GetOperatingSystem() (string, error) {
var h syscall.Handle var h syscall.Handle
@ -41,7 +42,8 @@ func GetOperatingSystem() (string, error) {
return ret, nil return ret, nil
} }
// No-op on Windows // IsContainerized returns true if we are running inside a container.
// No-op on Windows, always returns false.
func IsContainerized() (bool, error) { func IsContainerized() (bool, error) {
return false, nil return false, nil
} }

View file

@ -1,3 +1,6 @@
// Package parsers provides helper functions to parse and validate different type
// of string. It can be hosts, unix addresses, tcp addresses, filters, kernel
// operating system versions.
package parsers package parsers
import ( import (
@ -9,6 +12,8 @@ import (
"strings" "strings"
) )
// ParseHost parses the specified address and returns an address that will be used as the host.
// Depending of the address specified, will use the defaultTCPAddr or defaultUnixAddr
// FIXME: Change this not to receive default value as parameter // FIXME: Change this not to receive default value as parameter
func ParseHost(defaultTCPAddr, defaultUnixAddr, addr string) (string, error) { func ParseHost(defaultTCPAddr, defaultUnixAddr, addr string) (string, error) {
addr = strings.TrimSpace(addr) addr = strings.TrimSpace(addr)
@ -17,7 +22,7 @@ func ParseHost(defaultTCPAddr, defaultUnixAddr, addr string) (string, error) {
addr = fmt.Sprintf("unix://%s", defaultUnixAddr) addr = fmt.Sprintf("unix://%s", defaultUnixAddr)
} else { } else {
// Note - defaultTCPAddr already includes tcp:// prefix // Note - defaultTCPAddr already includes tcp:// prefix
addr = fmt.Sprintf("%s", defaultTCPAddr) addr = defaultTCPAddr
} }
} }
addrParts := strings.Split(addr, "://") addrParts := strings.Split(addr, "://")
@ -37,6 +42,10 @@ func ParseHost(defaultTCPAddr, defaultUnixAddr, addr string) (string, error) {
} }
} }
// ParseUnixAddr parses and validates that the specified address is a valid UNIX
// socket address. It returns a formatted UNIX socket address, either using the
// address parsed from addr, or the contents of defaultAddr if addr is a blank
// string.
func ParseUnixAddr(addr string, defaultAddr string) (string, error) { func ParseUnixAddr(addr string, defaultAddr string) (string, error) {
addr = strings.TrimPrefix(addr, "unix://") addr = strings.TrimPrefix(addr, "unix://")
if strings.Contains(addr, "://") { if strings.Contains(addr, "://") {
@ -48,6 +57,9 @@ func ParseUnixAddr(addr string, defaultAddr string) (string, error) {
return fmt.Sprintf("unix://%s", addr), nil return fmt.Sprintf("unix://%s", addr), nil
} }
// ParseTCPAddr parses and validates that the specified address is a valid TCP
// address. It returns a formatted TCP address, either using the address parsed
// from addr, or the contents of defaultAddr if addr is a blank string.
func ParseTCPAddr(addr string, defaultAddr string) (string, error) { func ParseTCPAddr(addr string, defaultAddr string) (string, error) {
addr = strings.TrimPrefix(addr, "tcp://") addr = strings.TrimPrefix(addr, "tcp://")
if strings.Contains(addr, "://") || addr == "" { if strings.Contains(addr, "://") || addr == "" {
@ -74,7 +86,7 @@ func ParseTCPAddr(addr string, defaultAddr string) (string, error) {
return fmt.Sprintf("tcp://%s:%d%s", host, p, u.Path), nil return fmt.Sprintf("tcp://%s:%d%s", host, p, u.Path), nil
} }
// Get a repos name and returns the right reposName + tag|digest // ParseRepositoryTag gets a repos name and returns the right reposName + tag|digest
// The tag can be confusing because of a port in a repository name. // The tag can be confusing because of a port in a repository name.
// Ex: localhost.localdomain:5000/samalba/hipache:latest // Ex: localhost.localdomain:5000/samalba/hipache:latest
// Digest ex: localhost:5000/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb // Digest ex: localhost:5000/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb
@ -94,6 +106,8 @@ func ParseRepositoryTag(repos string) (string, string) {
return repos, "" return repos, ""
} }
// PartParser parses and validates the specified string (data) using the specified template
// e.g. ip:public:private -> 192.168.0.1:80:8000
func PartParser(template, data string) (map[string]string, error) { func PartParser(template, data string) (map[string]string, error) {
// ip:public:private // ip:public:private
var ( var (
@ -115,6 +129,7 @@ func PartParser(template, data string) (map[string]string, error) {
return out, nil return out, nil
} }
// ParseKeyValueOpt parses and validates the specified string as a key/value pair (key=value)
func ParseKeyValueOpt(opt string) (string, string, error) { func ParseKeyValueOpt(opt string) (string, string, error) {
parts := strings.SplitN(opt, "=", 2) parts := strings.SplitN(opt, "=", 2)
if len(parts) != 2 { if len(parts) != 2 {
@ -123,6 +138,7 @@ func ParseKeyValueOpt(opt string) (string, string, error) {
return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
} }
// ParsePortRange parses and validates the specified string as a port-range (8000-9000)
func ParsePortRange(ports string) (uint64, uint64, error) { func ParsePortRange(ports string) (uint64, uint64, error) {
if ports == "" { if ports == "" {
return 0, 0, fmt.Errorf("Empty string specified for ports.") return 0, 0, fmt.Errorf("Empty string specified for ports.")
@ -148,6 +164,7 @@ func ParsePortRange(ports string) (uint64, uint64, error) {
return start, end, nil return start, end, nil
} }
// ParseLink parses and validates the specified string as a link format (name:alias)
func ParseLink(val string) (string, string, error) { func ParseLink(val string) (string, string, error) {
if val == "" { if val == "" {
return "", "", fmt.Errorf("empty string specified for links") return "", "", fmt.Errorf("empty string specified for links")

View file

@ -7,7 +7,7 @@ import (
func TestParseHost(t *testing.T) { func TestParseHost(t *testing.T) {
var ( var (
defaultHttpHost = "127.0.0.1" defaultHTTPHost = "127.0.0.1"
defaultUnix = "/var/run/docker.sock" defaultUnix = "/var/run/docker.sock"
) )
invalids := map[string]string{ invalids := map[string]string{
@ -32,12 +32,12 @@ func TestParseHost(t *testing.T) {
"fd://something": "fd://something", "fd://something": "fd://something",
} }
for invalidAddr, expectedError := range invalids { for invalidAddr, expectedError := range invalids {
if addr, err := ParseHost(defaultHttpHost, defaultUnix, invalidAddr); err == nil || err.Error() != expectedError { if addr, err := ParseHost(defaultHTTPHost, defaultUnix, invalidAddr); err == nil || err.Error() != expectedError {
t.Errorf("tcp %v address expected error %v return, got %s and addr %v", invalidAddr, expectedError, err, addr) t.Errorf("tcp %v address expected error %v return, got %s and addr %v", invalidAddr, expectedError, err, addr)
} }
} }
for validAddr, expectedAddr := range valids { for validAddr, expectedAddr := range valids {
if addr, err := ParseHost(defaultHttpHost, defaultUnix, validAddr); err != nil || addr != expectedAddr { if addr, err := ParseHost(defaultHTTPHost, defaultUnix, validAddr); err != nil || addr != expectedAddr {
t.Errorf("%v -> expected %v, got %v", validAddr, expectedAddr, addr) t.Errorf("%v -> expected %v, got %v", validAddr, expectedAddr, addr)
} }
} }

View file

@ -1,3 +1,6 @@
// Package pidfile provides structure and helper functions to create and remove
// PID file. A PID file is usually a file used to store the process ID of a
// running process.
package pidfile package pidfile
import ( import (
@ -8,11 +11,12 @@ import (
"strconv" "strconv"
) )
type PidFile struct { // PIDFile is a file used to store the process ID of a running process.
type PIDFile struct {
path string path string
} }
func checkPidFileAlreadyExists(path string) error { func checkPIDFileAlreadyExists(path string) error {
if pidString, err := ioutil.ReadFile(path); err == nil { if pidString, err := ioutil.ReadFile(path); err == nil {
if pid, err := strconv.Atoi(string(pidString)); err == nil { if pid, err := strconv.Atoi(string(pidString)); err == nil {
if _, err := os.Stat(filepath.Join("/proc", string(pid))); err == nil { if _, err := os.Stat(filepath.Join("/proc", string(pid))); err == nil {
@ -23,18 +27,20 @@ func checkPidFileAlreadyExists(path string) error {
return nil return nil
} }
func New(path string) (*PidFile, error) { // New creates a PIDfile using the specified path.
if err := checkPidFileAlreadyExists(path); err != nil { func New(path string) (*PIDFile, error) {
if err := checkPIDFileAlreadyExists(path); err != nil {
return nil, err return nil, err
} }
if err := ioutil.WriteFile(path, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil { if err := ioutil.WriteFile(path, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil {
return nil, err return nil, err
} }
return &PidFile{path: path}, nil return &PIDFile{path: path}, nil
} }
func (file PidFile) Remove() error { // Remove removes the PIDFile.
func (file PIDFile) Remove() error {
if err := os.Remove(file.path); err != nil { if err := os.Remove(file.path); err != nil {
return err return err
} }

View file

@ -24,7 +24,7 @@ func TestNewAndRemove(t *testing.T) {
} }
func TestRemoveInvalidPath(t *testing.T) { func TestRemoveInvalidPath(t *testing.T) {
file := PidFile{path: filepath.Join("foo", "bar")} file := PIDFile{path: filepath.Join("foo", "bar")}
if err := file.Remove(); err == nil { if err := file.Remove(); err == nil {
t.Fatal("Non-existing file doesn't give an error on delete") t.Fatal("Non-existing file doesn't give an error on delete")

View file

@ -19,6 +19,7 @@ const (
defaultTimeOut = 30 defaultTimeOut = 30
) )
// NewClient creates a new plugin client (http).
func NewClient(addr string, tlsConfig tlsconfig.Options) (*Client, error) { func NewClient(addr string, tlsConfig tlsconfig.Options) (*Client, error) {
tr := &http.Transport{} tr := &http.Transport{}
@ -33,11 +34,14 @@ func NewClient(addr string, tlsConfig tlsconfig.Options) (*Client, error) {
return &Client{&http.Client{Transport: tr}, protoAndAddr[1]}, nil return &Client{&http.Client{Transport: tr}, protoAndAddr[1]}, nil
} }
// Client represents a plugin client.
type Client struct { type Client struct {
http *http.Client http *http.Client // http client to use
addr string addr string // http address of the plugin
} }
// Call calls the specified method with the specified arguments for the plugin.
// It will retry for 30 seconds if a failure occurs when calling.
func (c *Client) Call(serviceMethod string, args interface{}, ret interface{}) error { func (c *Client) Call(serviceMethod string, args interface{}, ret interface{}) error {
return c.callWithRetry(serviceMethod, args, ret, true) return c.callWithRetry(serviceMethod, args, ret, true)
} }

View file

@ -12,22 +12,28 @@ import (
) )
var ( var (
// ErrNotFound plugin not found
ErrNotFound = errors.New("Plugin not found") ErrNotFound = errors.New("Plugin not found")
socketsPath = "/run/docker/plugins" socketsPath = "/run/docker/plugins"
specsPaths = []string{"/etc/docker/plugins", "/usr/lib/docker/plugins"} specsPaths = []string{"/etc/docker/plugins", "/usr/lib/docker/plugins"}
) )
// Registry defines behavior of a registry of plugins.
type Registry interface { type Registry interface {
// Plugins lists all plugins.
Plugins() ([]*Plugin, error) Plugins() ([]*Plugin, error)
// Plugin returns the plugin registered with the given name (or returns an error).
Plugin(name string) (*Plugin, error) Plugin(name string) (*Plugin, error)
} }
// LocalRegistry defines a registry that is local (using unix socket).
type LocalRegistry struct{} type LocalRegistry struct{}
func newLocalRegistry() LocalRegistry { func newLocalRegistry() LocalRegistry {
return LocalRegistry{} return LocalRegistry{}
} }
// Plugin returns the plugin registered with the given name (or returns an error).
func (l *LocalRegistry) Plugin(name string) (*Plugin, error) { func (l *LocalRegistry) Plugin(name string) (*Plugin, error) {
socketpaths := pluginPaths(socketsPath, name, ".sock") socketpaths := pluginPaths(socketsPath, name, ".sock")

View file

@ -1,3 +1,25 @@
// Package plugins provides structures and helper functions to manage Docker
// plugins.
//
// Docker discovers plugins by looking for them in the plugin directory whenever
// a user or container tries to use one by name. UNIX domain socket files must
// be located under /run/docker/plugins, whereas spec files can be located
// either under /etc/docker/plugins or /usr/lib/docker/plugins. This is handled
// by the Registry interface, which lets you list all plugins or get a plugin by
// its name if it exists.
//
// The plugins need to implement an HTTP server and bind this to the UNIX socket
// or the address specified in the spec files.
// A handshake is send at /Plugin.Activate, and plugins are expected to return
// a Manifest with a list of of Docker subsystems which this plugin implements.
//
// In order to use a plugins, you can use the ``Get`` with the name of the
// plugin and the subsystem it implements.
//
// plugin, err := plugins.Get("example", "VolumeDriver")
// if err != nil {
// return fmt.Errorf("Error looking up volume plugin example: %v", err)
// }
package plugins package plugins
import ( import (
@ -9,6 +31,7 @@ import (
) )
var ( var (
// ErrNotImplements is returned if the plugin does not implement the requested driver.
ErrNotImplements = errors.New("Plugin does not implement the requested driver") ErrNotImplements = errors.New("Plugin does not implement the requested driver")
) )
@ -22,16 +45,24 @@ var (
extpointHandlers = make(map[string]func(string, *Client)) extpointHandlers = make(map[string]func(string, *Client))
) )
// Manifest lists what a plugin implements.
type Manifest struct { type Manifest struct {
// List of subsystem the plugin implements.
Implements []string Implements []string
} }
// Plugin is the definition of a docker plugin.
type Plugin struct { type Plugin struct {
Name string `json:"-"` // Name of the plugin
Addr string Name string `json:"-"`
// Address of the plugin
Addr string
// TLS configuration of the plugin
TLSConfig tlsconfig.Options TLSConfig tlsconfig.Options
Client *Client `json:"-"` // Client attached to the plugin
Manifest *Manifest `json:"-"` Client *Client `json:"-"`
// Manifest of the plugin (see above)
Manifest *Manifest `json:"-"`
} }
func newLocalPlugin(name, addr string) *Plugin { func newLocalPlugin(name, addr string) *Plugin {
@ -96,6 +127,7 @@ func get(name string) (*Plugin, error) {
return pl, nil return pl, nil
} }
// Get returns the plugin given the specified name and requested implementation.
func Get(name, imp string) (*Plugin, error) { func Get(name, imp string) (*Plugin, error) {
pl, err := get(name) pl, err := get(name)
if err != nil { if err != nil {
@ -110,6 +142,7 @@ func Get(name, imp string) (*Plugin, error) {
return nil, ErrNotImplements return nil, ErrNotImplements
} }
// Handle adds the specified function to the extpointHandlers.
func Handle(iface string, fn func(string, *Client)) { func Handle(iface string, fn func(string, *Client)) {
extpointHandlers[iface] = fn extpointHandlers[iface] = fn
} }

View file

@ -18,14 +18,15 @@ import (
) )
var ( var (
// Pool which returns bufio.Reader with a 32K buffer // BufioReader32KPool is a pool which returns bufio.Reader with a 32K buffer.
BufioReader32KPool *BufioReaderPool BufioReader32KPool *BufioReaderPool
// Pool which returns bufio.Writer with a 32K buffer // BufioWriter32KPool is a pool which returns bufio.Writer with a 32K buffer.
BufioWriter32KPool *BufioWriterPool BufioWriter32KPool *BufioWriterPool
) )
const buffer32K = 32 * 1024 const buffer32K = 32 * 1024
// BufioReaderPool is a bufio reader that uses sync.Pool.
type BufioReaderPool struct { type BufioReaderPool struct {
pool sync.Pool pool sync.Pool
} }
@ -57,7 +58,7 @@ func (bufPool *BufioReaderPool) Put(b *bufio.Reader) {
bufPool.pool.Put(b) bufPool.pool.Put(b)
} }
// Copy is a convenience wrapper which uses a buffer to avoid allocation in io.Copy // Copy is a convenience wrapper which uses a buffer to avoid allocation in io.Copy.
func Copy(dst io.Writer, src io.Reader) (written int64, err error) { func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
buf := BufioReader32KPool.Get(src) buf := BufioReader32KPool.Get(src)
written, err = io.Copy(dst, buf) written, err = io.Copy(dst, buf)
@ -77,6 +78,7 @@ func (bufPool *BufioReaderPool) NewReadCloserWrapper(buf *bufio.Reader, r io.Rea
}) })
} }
// BufioWriterPool is a bufio writer that uses sync.Pool.
type BufioWriterPool struct { type BufioWriterPool struct {
pool sync.Pool pool sync.Pool
} }

View file

@ -1,3 +1,5 @@
// Package progressreader provides a Reader with a progress bar that can be
// printed out using the streamformatter package.
package progressreader package progressreader
import ( import (
@ -7,7 +9,7 @@ import (
"github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/streamformatter"
) )
// Reader with progress bar // Config contains the configuration for a Reader with progress bar.
type Config struct { type Config struct {
In io.ReadCloser // Stream to read from In io.ReadCloser // Stream to read from
Out io.Writer // Where to send progress bar to Out io.Writer // Where to send progress bar to
@ -20,6 +22,7 @@ type Config struct {
Action string Action string
} }
// New creates a new Config.
func New(newReader Config) *Config { func New(newReader Config) *Config {
return &newReader return &newReader
} }
@ -48,6 +51,7 @@ func (config *Config) Read(p []byte) (n int, err error) {
return read, err return read, err
} }
// Close closes the reader (Config).
func (config *Config) Close() error { func (config *Config) Close() error {
if config.Current < config.Size { if config.Current < config.Size {
//print a full progress bar when closing prematurely //print a full progress bar when closing prematurely

View file

@ -1,3 +1,5 @@
// Package proxy provides a network Proxy interface and implementations for TCP
// and UDP.
package proxy package proxy
import ( import (
@ -5,18 +7,24 @@ import (
"net" "net"
) )
// Proxy defines the behavior of a proxy. It forwards traffic back and forth
// between two endpoints : the frontend and the backend.
// It can be used to do software port-mapping between two addresses.
// e.g. forward all traffic between the frontend (host) 127.0.0.1:3000
// to the backend (container) at 172.17.42.108:4000.
type Proxy interface { type Proxy interface {
// Start forwarding traffic back and forth the front and back-end // Run starts forwarding traffic back and forth between the front
// addresses. // and back-end addresses.
Run() Run()
// Stop forwarding traffic and close both ends of the Proxy. // Close stops forwarding traffic and close both ends of the Proxy.
Close() Close()
// Return the address on which the proxy is listening. // FrontendAddr returns the address on which the proxy is listening.
FrontendAddr() net.Addr FrontendAddr() net.Addr
// Return the proxied address. // BackendAddr returns the proxied address.
BackendAddr() net.Addr BackendAddr() net.Addr
} }
// NewProxy creates a Proxy according to the specified frontendAddr and backendAddr.
func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) { func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
switch frontendAddr.(type) { switch frontendAddr.(type) {
case *net.UDPAddr: case *net.UDPAddr:

View file

@ -4,16 +4,25 @@ import (
"net" "net"
) )
// StubProxy is a proxy that is a stub (does nothing).
type StubProxy struct { type StubProxy struct {
frontendAddr net.Addr frontendAddr net.Addr
backendAddr net.Addr backendAddr net.Addr
} }
func (p *StubProxy) Run() {} // Run does nothing.
func (p *StubProxy) Close() {} func (p *StubProxy) Run() {}
func (p *StubProxy) FrontendAddr() net.Addr { return p.frontendAddr }
func (p *StubProxy) BackendAddr() net.Addr { return p.backendAddr }
// Close does nothing.
func (p *StubProxy) Close() {}
// FrontendAddr returns the frontend address.
func (p *StubProxy) FrontendAddr() net.Addr { return p.frontendAddr }
// BackendAddr returns the backend address.
func (p *StubProxy) BackendAddr() net.Addr { return p.backendAddr }
// NewStubProxy creates a new StubProxy
func NewStubProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) { func NewStubProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
return &StubProxy{ return &StubProxy{
frontendAddr: frontendAddr, frontendAddr: frontendAddr,

View file

@ -8,12 +8,15 @@ import (
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
) )
// TCPProxy is a proxy for TCP connections. It implements the Proxy interface to
// handle TCP traffic forwarding between the frontend and backend addresses.
type TCPProxy struct { type TCPProxy struct {
listener *net.TCPListener listener *net.TCPListener
frontendAddr *net.TCPAddr frontendAddr *net.TCPAddr
backendAddr *net.TCPAddr backendAddr *net.TCPAddr
} }
// NewTCPProxy creates a new TCPProxy.
func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) { func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
listener, err := net.ListenTCP("tcp", frontendAddr) listener, err := net.ListenTCP("tcp", frontendAddr)
if err != nil { if err != nil {
@ -53,7 +56,7 @@ func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
go broker(client, backend) go broker(client, backend)
go broker(backend, client) go broker(backend, client)
var transferred int64 = 0 var transferred int64
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
select { select {
case written := <-event: case written := <-event:
@ -72,6 +75,7 @@ func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
backend.Close() backend.Close()
} }
// Run starts forwarding the traffic using TCP.
func (proxy *TCPProxy) Run() { func (proxy *TCPProxy) Run() {
quit := make(chan bool) quit := make(chan bool)
defer close(quit) defer close(quit)
@ -85,6 +89,11 @@ func (proxy *TCPProxy) Run() {
} }
} }
func (proxy *TCPProxy) Close() { proxy.listener.Close() } // Close stops forwarding the traffic.
func (proxy *TCPProxy) Close() { proxy.listener.Close() }
// FrontendAddr returns the TCP address on which the proxy is listening.
func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr } func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
func (proxy *TCPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
// BackendAddr returns the TCP proxied address.
func (proxy *TCPProxy) BackendAddr() net.Addr { return proxy.backendAddr }

View file

@ -12,8 +12,10 @@ import (
) )
const ( const (
// UDPConnTrackTimeout is the timeout used for UDP connection tracking
UDPConnTrackTimeout = 90 * time.Second UDPConnTrackTimeout = 90 * time.Second
UDPBufSize = 65507 // UDPBufSize is the buffer size for the UDP proxy
UDPBufSize = 65507
) )
// A net.Addr where the IP is split into two fields so you can use it as a key // A net.Addr where the IP is split into two fields so you can use it as a key
@ -41,6 +43,9 @@ func newConnTrackKey(addr *net.UDPAddr) *connTrackKey {
type connTrackMap map[connTrackKey]*net.UDPConn type connTrackMap map[connTrackKey]*net.UDPConn
// UDPProxy is proxy for which handles UDP datagrams. It implements the Proxy
// interface to handle UDP traffic forwarding between the frontend and backend
// addresses.
type UDPProxy struct { type UDPProxy struct {
listener *net.UDPConn listener *net.UDPConn
frontendAddr *net.UDPAddr frontendAddr *net.UDPAddr
@ -49,6 +54,7 @@ type UDPProxy struct {
connTrackLock sync.Mutex connTrackLock sync.Mutex
} }
// NewUDPProxy creates a new UDPProxy.
func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) { func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) {
listener, err := net.ListenUDP("udp", frontendAddr) listener, err := net.ListenUDP("udp", frontendAddr)
if err != nil { if err != nil {
@ -96,6 +102,7 @@ func (proxy *UDPProxy) replyLoop(proxyConn *net.UDPConn, clientAddr *net.UDPAddr
} }
} }
// Run starts forwarding the traffic using UDP.
func (proxy *UDPProxy) Run() { func (proxy *UDPProxy) Run() {
readBuf := make([]byte, UDPBufSize) readBuf := make([]byte, UDPBufSize)
for { for {
@ -135,6 +142,7 @@ func (proxy *UDPProxy) Run() {
} }
} }
// Close stops forwarding the traffic.
func (proxy *UDPProxy) Close() { func (proxy *UDPProxy) Close() {
proxy.listener.Close() proxy.listener.Close()
proxy.connTrackLock.Lock() proxy.connTrackLock.Lock()
@ -144,8 +152,11 @@ func (proxy *UDPProxy) Close() {
} }
} }
// FrontendAddr returns the UDP address on which the proxy is listening.
func (proxy *UDPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr } func (proxy *UDPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
func (proxy *UDPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
// BackendAddr returns the proxied UDP address.
func (proxy *UDPProxy) BackendAddr() net.Addr { return proxy.backendAddr }
func isClosedError(err error) bool { func isClosedError(err error) bool {
/* This comparison is ugly, but unfortunately, net.go doesn't export errClosing. /* This comparison is ugly, but unfortunately, net.go doesn't export errClosing.

View file

@ -1,3 +1,5 @@
// Package signal provides helper functions for dealing with signals across
// various operating systems.
package signal package signal
import ( import (
@ -5,6 +7,7 @@ import (
"os/signal" "os/signal"
) )
// CatchAll catches all signals and relays them to the specified channel.
func CatchAll(sigc chan os.Signal) { func CatchAll(sigc chan os.Signal) {
handledSigs := []os.Signal{} handledSigs := []os.Signal{}
for _, s := range SignalMap { for _, s := range SignalMap {
@ -13,6 +16,7 @@ func CatchAll(sigc chan os.Signal) {
signal.Notify(sigc, handledSigs...) signal.Notify(sigc, handledSigs...)
} }
// StopCatch stops catching the signals and closes the specified channel.
func StopCatch(sigc chan os.Signal) { func StopCatch(sigc chan os.Signal) {
signal.Stop(sigc) signal.Stop(sigc)
close(sigc) close(sigc)

View file

@ -4,6 +4,7 @@ import (
"syscall" "syscall"
) )
// SignalMap is a map of Darwin signals.
var SignalMap = map[string]syscall.Signal{ var SignalMap = map[string]syscall.Signal{
"ABRT": syscall.SIGABRT, "ABRT": syscall.SIGABRT,
"ALRM": syscall.SIGALRM, "ALRM": syscall.SIGALRM,

View file

@ -4,6 +4,7 @@ import (
"syscall" "syscall"
) )
// SignalMap is a map of FreeBSD signals.
var SignalMap = map[string]syscall.Signal{ var SignalMap = map[string]syscall.Signal{
"ABRT": syscall.SIGABRT, "ABRT": syscall.SIGABRT,
"ALRM": syscall.SIGALRM, "ALRM": syscall.SIGALRM,

View file

@ -4,6 +4,7 @@ import (
"syscall" "syscall"
) )
// SignalMap is a map of Linux signals.
var SignalMap = map[string]syscall.Signal{ var SignalMap = map[string]syscall.Signal{
"ABRT": syscall.SIGABRT, "ABRT": syscall.SIGABRT,
"ALRM": syscall.SIGALRM, "ALRM": syscall.SIGALRM,

View file

@ -8,5 +8,9 @@ import (
// Signals used in api/client (no windows equivalent, use // Signals used in api/client (no windows equivalent, use
// invalid signals so they don't get handled) // invalid signals so they don't get handled)
// SIGCHLD is a signal sent to a process when a child process terminates, is interrupted, or resumes after being interrupted.
const SIGCHLD = syscall.SIGCHLD const SIGCHLD = syscall.SIGCHLD
// SIGWINCH is a signal sent to a process when its controlling terminal changes its size
const SIGWINCH = syscall.SIGWINCH const SIGWINCH = syscall.SIGWINCH

View file

@ -6,4 +6,5 @@ import (
"syscall" "syscall"
) )
// SignalMap is an empty map of signals for unsupported platform.
var SignalMap = map[string]syscall.Signal{} var SignalMap = map[string]syscall.Signal{}

View file

@ -8,5 +8,7 @@ import (
// Signals used in api/client (no windows equivalent, use // Signals used in api/client (no windows equivalent, use
// invalid signals so they don't get handled) // invalid signals so they don't get handled)
const SIGCHLD = syscall.Signal(0xff) const (
const SIGWINCH = syscall.Signal(0xff) SIGCHLD = syscall.Signal(0xff)
SIGWINCH = syscall.Signal(0xff)
)

View file

@ -55,6 +55,7 @@ func Trap(cleanup func()) {
}() }()
} }
// DumpStacks dumps the runtime stack.
func DumpStacks() { func DumpStacks() {
buf := make([]byte, 16384) buf := make([]byte, 16384)
buf = buf[:runtime.Stack(buf, true)] buf = buf[:runtime.Stack(buf, true)]

View file

@ -1,3 +1,5 @@
// Package sockets provides helper functions to create and configure Unix or TCP
// sockets.
package sockets package sockets
import ( import (
@ -9,7 +11,12 @@ import (
"github.com/docker/docker/pkg/listenbuffer" "github.com/docker/docker/pkg/listenbuffer"
) )
func NewTcpSocket(addr string, tlsConfig *tls.Config, activate <-chan struct{}) (net.Listener, error) { // NewTCPSocket creates a TCP socket listener with the specified address and
// and the specified tls configuration. If TLSConfig is set, will encapsulate the
// TCP listener inside a TLS one.
// The channel passed is used to activate the listenbuffer when the caller is ready
// to accept connections.
func NewTCPSocket(addr string, tlsConfig *tls.Config, activate <-chan struct{}) (net.Listener, error) {
l, err := listenbuffer.NewListenBuffer("tcp", addr, activate) l, err := listenbuffer.NewListenBuffer("tcp", addr, activate)
if err != nil { if err != nil {
return nil, err return nil, err
@ -21,6 +28,10 @@ func NewTcpSocket(addr string, tlsConfig *tls.Config, activate <-chan struct{})
return l, nil return l, nil
} }
// ConfigureTCPTransport configures the specified Transport according to the
// specified proto and addr.
// If the proto is unix (using a unix socket to communicate) the compression
// is disabled.
func ConfigureTCPTransport(tr *http.Transport, proto, addr string) { func ConfigureTCPTransport(tr *http.Transport, proto, addr string) {
// Why 32? See https://github.com/docker/docker/pull/8035. // Why 32? See https://github.com/docker/docker/pull/8035.
timeout := 32 * time.Second timeout := 32 * time.Second

View file

@ -14,6 +14,9 @@ import (
"github.com/opencontainers/runc/libcontainer/user" "github.com/opencontainers/runc/libcontainer/user"
) )
// NewUnixSocket creates a unix socket with the specified path and group.
// The channel passed is used to activate the listenbuffer when the caller is ready
// to accept connections.
func NewUnixSocket(path, group string, activate <-chan struct{}) (net.Listener, error) { func NewUnixSocket(path, group string, activate <-chan struct{}) (net.Listener, error) {
if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) { if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) {
return nil, err return nil, err

View file

@ -1,3 +1,4 @@
// Package streamformatter provides helper functions to format a stream.
package streamformatter package streamformatter
import ( import (
@ -8,6 +9,7 @@ import (
"github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/jsonmessage"
) )
// StreamFormatter formats a stream, optionally using JSON.
type StreamFormatter struct { type StreamFormatter struct {
json bool json bool
} }
@ -26,6 +28,7 @@ const streamNewline = "\r\n"
var streamNewlineBytes = []byte(streamNewline) var streamNewlineBytes = []byte(streamNewline)
// FormatStream formats the specified stream.
func (sf *StreamFormatter) FormatStream(str string) []byte { func (sf *StreamFormatter) FormatStream(str string) []byte {
if sf.json { if sf.json {
b, err := json.Marshal(&jsonmessage.JSONMessage{Stream: str}) b, err := json.Marshal(&jsonmessage.JSONMessage{Stream: str})
@ -37,6 +40,7 @@ func (sf *StreamFormatter) FormatStream(str string) []byte {
return []byte(str + "\r") return []byte(str + "\r")
} }
// FormatStatus formats the specified objects according to the specified format (and id).
func (sf *StreamFormatter) FormatStatus(id, format string, a ...interface{}) []byte { func (sf *StreamFormatter) FormatStatus(id, format string, a ...interface{}) []byte {
str := fmt.Sprintf(format, a...) str := fmt.Sprintf(format, a...)
if sf.json { if sf.json {
@ -49,6 +53,7 @@ func (sf *StreamFormatter) FormatStatus(id, format string, a ...interface{}) []b
return []byte(str + streamNewline) return []byte(str + streamNewline)
} }
// FormatError formats the specifed error.
func (sf *StreamFormatter) FormatError(err error) []byte { func (sf *StreamFormatter) FormatError(err error) []byte {
if sf.json { if sf.json {
jsonError, ok := err.(*jsonmessage.JSONError) jsonError, ok := err.(*jsonmessage.JSONError)
@ -63,6 +68,7 @@ func (sf *StreamFormatter) FormatError(err error) []byte {
return []byte("Error: " + err.Error() + streamNewline) return []byte("Error: " + err.Error() + streamNewline)
} }
// FormatProgress formats the progress information for a specified action.
func (sf *StreamFormatter) FormatProgress(id, action string, progress *jsonmessage.JSONProgress) []byte { func (sf *StreamFormatter) FormatProgress(id, action string, progress *jsonmessage.JSONProgress) []byte {
if progress == nil { if progress == nil {
progress = &jsonmessage.JSONProgress{} progress = &jsonmessage.JSONProgress{}
@ -86,12 +92,13 @@ func (sf *StreamFormatter) FormatProgress(id, action string, progress *jsonmessa
return []byte(action + " " + progress.String() + endl) return []byte(action + " " + progress.String() + endl)
} }
type StdoutFormater struct { // StdoutFormatter is a streamFormatter that writes to the standard output.
type StdoutFormatter struct {
io.Writer io.Writer
*StreamFormatter *StreamFormatter
} }
func (sf *StdoutFormater) Write(buf []byte) (int, error) { func (sf *StdoutFormatter) Write(buf []byte) (int, error) {
formattedBuf := sf.StreamFormatter.FormatStream(string(buf)) formattedBuf := sf.StreamFormatter.FormatStream(string(buf))
n, err := sf.Writer.Write(formattedBuf) n, err := sf.Writer.Write(formattedBuf)
if n != len(formattedBuf) { if n != len(formattedBuf) {
@ -100,12 +107,13 @@ func (sf *StdoutFormater) Write(buf []byte) (int, error) {
return len(buf), err return len(buf), err
} }
type StderrFormater struct { // StderrFormatter is a streamFormatter that writes to the standard error.
type StderrFormatter struct {
io.Writer io.Writer
*StreamFormatter *StreamFormatter
} }
func (sf *StderrFormater) Write(buf []byte) (int, error) { func (sf *StderrFormatter) Write(buf []byte) (int, error) {
formattedBuf := sf.StreamFormatter.FormatStream("\033[91m" + string(buf) + "\033[0m") formattedBuf := sf.StreamFormatter.FormatStream("\033[91m" + string(buf) + "\033[0m")
n, err := sf.Writer.Write(formattedBuf) n, err := sf.Writer.Write(formattedBuf)
if n != len(formattedBuf) { if n != len(formattedBuf) {

View file

@ -1,3 +1,4 @@
// Package stringid provides helper functions for dealing with string identifiers
package stringid package stringid
import ( import (
@ -12,7 +13,7 @@ const shortLen = 12
var validShortID = regexp.MustCompile("^[a-z0-9]{12}$") var validShortID = regexp.MustCompile("^[a-z0-9]{12}$")
// Determine if an arbitrary string *looks like* a short ID. // IsShortID determines if an arbitrary string *looks like* a short ID.
func IsShortID(id string) bool { func IsShortID(id string) bool {
return validShortID.MatchString(id) return validShortID.MatchString(id)
} }
@ -29,7 +30,7 @@ func TruncateID(id string) string {
return id[:trimTo] return id[:trimTo]
} }
// GenerateRandomID returns an unique id // GenerateRandomID returns an unique id.
func GenerateRandomID() string { func GenerateRandomID() string {
for { for {
id := make([]byte, 32) id := make([]byte, 32)

View file

@ -1,3 +1,4 @@
// Package stringutils provides helper functions for dealing with strings.
package stringutils package stringutils
import ( import (
@ -8,7 +9,7 @@ import (
"github.com/docker/docker/pkg/random" "github.com/docker/docker/pkg/random"
) )
// Generate alpha only random stirng with length n // GenerateRandomAlphaOnlyString generates an alphabetical random string with length n.
func GenerateRandomAlphaOnlyString(n int) string { func GenerateRandomAlphaOnlyString(n int) string {
// make a really long string // make a really long string
letters := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") letters := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
@ -20,8 +21,8 @@ func GenerateRandomAlphaOnlyString(n int) string {
return string(b) return string(b)
} }
// Generate Ascii random stirng with length n // GenerateRandomASCIIString generates an ASCII random stirng with length n.
func GenerateRandomAsciiString(n int) string { func GenerateRandomASCIIString(n int) string {
chars := "abcdefghijklmnopqrstuvwxyz" + chars := "abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"~!@#$%^&*()-_+={}[]\\|<,>.?/\"';:` " "~!@#$%^&*()-_+={}[]\\|<,>.?/\"';:` "
@ -32,7 +33,7 @@ func GenerateRandomAsciiString(n int) string {
return string(res) return string(res)
} }
// Truncate a string to maxlen // Truncate truncates a string to maxlen.
func Truncate(s string, maxlen int) string { func Truncate(s string, maxlen int) string {
if len(s) <= maxlen { if len(s) <= maxlen {
return s return s
@ -40,7 +41,7 @@ func Truncate(s string, maxlen int) string {
return s[:maxlen] return s[:maxlen]
} }
// Test wheather a string is contained in a slice of strings or not. // InSlice tests whether a string is contained in a slice of strings or not.
// Comparison is case insensitive // Comparison is case insensitive
func InSlice(slice []string, s string) bool { func InSlice(slice []string, s string) bool {
for _, ss := range slice { for _, ss := range slice {
@ -73,8 +74,8 @@ func quote(word string, buf *bytes.Buffer) {
buf.WriteString("'") buf.WriteString("'")
} }
// Take a list of strings and escape them so they will be handled right // ShellQuoteArguments takes a list of strings and escapes them so they will be
// when passed as arguments to an program via a shell // handled right when passed as arguments to an program via a shell
func ShellQuoteArguments(args []string) string { func ShellQuoteArguments(args []string) string {
var buf bytes.Buffer var buf bytes.Buffer
for i, arg := range args { for i, arg := range args {

View file

@ -43,15 +43,15 @@ func TestGenerateRandomAlphaOnlyStringUniqueness(t *testing.T) {
} }
func TestGenerateRandomAsciiStringLength(t *testing.T) { func TestGenerateRandomAsciiStringLength(t *testing.T) {
testLengthHelper(GenerateRandomAsciiString, t) testLengthHelper(GenerateRandomASCIIString, t)
} }
func TestGenerateRandomAsciiStringUniqueness(t *testing.T) { func TestGenerateRandomAsciiStringUniqueness(t *testing.T) {
testUniquenessHelper(GenerateRandomAsciiString, t) testUniquenessHelper(GenerateRandomASCIIString, t)
} }
func TestGenerateRandomAsciiStringIsAscii(t *testing.T) { func TestGenerateRandomAsciiStringIsAscii(t *testing.T) {
str := GenerateRandomAsciiString(64) str := GenerateRandomASCIIString(64)
if !isASCII(str) { if !isASCII(str) {
t.Fatalf("%s contained non-ascii characters", str) t.Fatalf("%s contained non-ascii characters", str)
} }

View file

@ -6,9 +6,10 @@ import (
"os" "os"
) )
var SdNotifyNoSocket = errors.New("No socket") // ErrSdNotifyNoSocket is an error returned if no socket was specified.
var ErrSdNotifyNoSocket = errors.New("No socket")
// Send a message to the init daemon. It is common to ignore the error. // SdNotify sends a message to the init daemon. It is common to ignore the return value.
func SdNotify(state string) error { func SdNotify(state string) error {
socketAddr := &net.UnixAddr{ socketAddr := &net.UnixAddr{
Name: os.Getenv("NOTIFY_SOCKET"), Name: os.Getenv("NOTIFY_SOCKET"),
@ -16,7 +17,7 @@ func SdNotify(state string) error {
} }
if socketAddr.Name == "" { if socketAddr.Name == "" {
return SdNotifyNoSocket return ErrSdNotifyNoSocket
} }
conn, err := net.DialUnix(socketAddr.Net, nil, socketAddr) conn, err := net.DialUnix(socketAddr.Net, nil, socketAddr)

View file

@ -1,3 +1,5 @@
// Package tailfile provides helper functinos to read the nth lines of any
// ReadSeeker.
package tailfile package tailfile
import ( import (
@ -10,9 +12,11 @@ import (
const blockSize = 1024 const blockSize = 1024
var eol = []byte("\n") var eol = []byte("\n")
var ErrNonPositiveLinesNumber = errors.New("Lines number must be positive")
//TailFile returns last n lines of file f // ErrNonPositiveLinesNumber is an error returned if the lines number was negative.
var ErrNonPositiveLinesNumber = errors.New("The number of lines to extract from the file must be positive")
//TailFile returns last n lines of reader f (could be a fil).
func TailFile(f io.ReadSeeker, n int) ([][]byte, error) { func TailFile(f io.ReadSeeker, n int) ([][]byte, error) {
if n <= 0 { if n <= 0 {
return nil, ErrNonPositiveLinesNumber return nil, ErrNonPositiveLinesNumber

View file

@ -1,9 +1,10 @@
package tarsum package tarsum
// This interface extends TarSum by adding the Remove method. In general // BuilderContext is an interface extending TarSum by adding the Remove method.
// there was concern about adding this method to TarSum itself so instead // In general there was concern about adding this method to TarSum itself
// it is being added just to "BuilderContext" which will then only be used // so instead it is being added just to "BuilderContext" which will then
// during the .dockerignore file processing - see builder/evaluator.go // only be used during the .dockerignore file processing
// - see builder/evaluator.go
type BuilderContext interface { type BuilderContext interface {
TarSum TarSum
Remove(string) Remove(string)

View file

@ -2,7 +2,9 @@ package tarsum
import "sort" import "sort"
// This info will be accessed through interface so the actual name and sum cannot be medled with // FileInfoSumInterface provides an interface for accessing file checksum
// information within a tar file. This info is accessed through interface
// so the actual name and sum cannot be medled with.
type FileInfoSumInterface interface { type FileInfoSumInterface interface {
// File name // File name
Name() string Name() string
@ -28,9 +30,10 @@ func (fis fileInfoSum) Pos() int64 {
return fis.pos return fis.pos
} }
// FileInfoSums provides a list of FileInfoSumInterfaces.
type FileInfoSums []FileInfoSumInterface type FileInfoSums []FileInfoSumInterface
// GetFile returns the first FileInfoSumInterface with a matching name // GetFile returns the first FileInfoSumInterface with a matching name.
func (fis FileInfoSums) GetFile(name string) FileInfoSumInterface { func (fis FileInfoSums) GetFile(name string) FileInfoSumInterface {
for i := range fis { for i := range fis {
if fis[i].Name() == name { if fis[i].Name() == name {
@ -40,7 +43,7 @@ func (fis FileInfoSums) GetFile(name string) FileInfoSumInterface {
return nil return nil
} }
// GetAllFile returns a FileInfoSums with all matching names // GetAllFile returns a FileInfoSums with all matching names.
func (fis FileInfoSums) GetAllFile(name string) FileInfoSums { func (fis FileInfoSums) GetAllFile(name string) FileInfoSums {
f := FileInfoSums{} f := FileInfoSums{}
for i := range fis { for i := range fis {
@ -51,6 +54,7 @@ func (fis FileInfoSums) GetAllFile(name string) FileInfoSums {
return f return f
} }
// GetDuplicatePaths returns a FileInfoSums with all duplicated paths.
func (fis FileInfoSums) GetDuplicatePaths() (dups FileInfoSums) { func (fis FileInfoSums) GetDuplicatePaths() (dups FileInfoSums) {
seen := make(map[string]int, len(fis)) // allocate earl. no need to grow this map. seen := make(map[string]int, len(fis)) // allocate earl. no need to grow this map.
for i := range fis { for i := range fis {
@ -64,17 +68,23 @@ func (fis FileInfoSums) GetDuplicatePaths() (dups FileInfoSums) {
return dups return dups
} }
func (fis FileInfoSums) Len() int { return len(fis) } // Len returns the size of the FileInfoSums.
func (fis FileInfoSums) Len() int { return len(fis) }
// Swap swaps two FileInfoSum values if a FileInfoSums list.
func (fis FileInfoSums) Swap(i, j int) { fis[i], fis[j] = fis[j], fis[i] } func (fis FileInfoSums) Swap(i, j int) { fis[i], fis[j] = fis[j], fis[i] }
// SortByPos sorts FileInfoSums content by position.
func (fis FileInfoSums) SortByPos() { func (fis FileInfoSums) SortByPos() {
sort.Sort(byPos{fis}) sort.Sort(byPos{fis})
} }
// SortByNames sorts FileInfoSums content by name.
func (fis FileInfoSums) SortByNames() { func (fis FileInfoSums) SortByNames() {
sort.Sort(byName{fis}) sort.Sort(byName{fis})
} }
// SortBySums sorts FileInfoSums content by sums.
func (fis FileInfoSums) SortBySums() { func (fis FileInfoSums) SortBySums() {
dups := fis.GetDuplicatePaths() dups := fis.GetDuplicatePaths()
if len(dups) > 0 { if len(dups) > 0 {

View file

@ -1,3 +1,20 @@
// Package tarsum provides algorithms to perform checksum calculation on
// filesystem layers.
//
// The transportation of filesystems, regarding Docker, is done with tar(1)
// archives. There are a variety of tar serialization formats [2], and a key
// concern here is ensuring a repeatable checksum given a set of inputs from a
// generic tar archive. Types of transportation include distribution to and from a
// registry endpoint, saving and loading through commands or Docker daemon APIs,
// transferring the build context from client to Docker daemon, and committing the
// filesystem of a container to become an image.
//
// As tar archives are used for transit, but not preserved in many situations, the
// focus of the algorithm is to ensure the integrity of the preserved filesystem,
// while maintaining a deterministic accountability. This includes neither
// constraining the ordering or manipulation of the files during the creation or
// unpacking of the archive, nor include additional metadata state about the file
// system attributes.
package tarsum package tarsum
import ( import (
@ -30,7 +47,8 @@ func NewTarSum(r io.Reader, dc bool, v Version) (TarSum, error) {
return NewTarSumHash(r, dc, v, DefaultTHash) return NewTarSumHash(r, dc, v, DefaultTHash)
} }
// Create a new TarSum, providing a THash to use rather than the DefaultTHash // NewTarSumHash creates a new TarSum, providing a THash to use rather than
// the DefaultTHash.
func NewTarSumHash(r io.Reader, dc bool, v Version, tHash THash) (TarSum, error) { func NewTarSumHash(r io.Reader, dc bool, v Version, tHash THash) (TarSum, error) {
headerSelector, err := getTarHeaderSelector(v) headerSelector, err := getTarHeaderSelector(v)
if err != nil { if err != nil {
@ -41,7 +59,7 @@ func NewTarSumHash(r io.Reader, dc bool, v Version, tHash THash) (TarSum, error)
return ts, err return ts, err
} }
// Create a new TarSum using the provided TarSum version+hash label. // NewTarSumForLabel creates a new TarSum using the provided TarSum version+hash label.
func NewTarSumForLabel(r io.Reader, disableCompression bool, label string) (TarSum, error) { func NewTarSumForLabel(r io.Reader, disableCompression bool, label string) (TarSum, error) {
parts := strings.SplitN(label, "+", 2) parts := strings.SplitN(label, "+", 2)
if len(parts) != 2 { if len(parts) != 2 {
@ -66,7 +84,7 @@ func NewTarSumForLabel(r io.Reader, disableCompression bool, label string) (TarS
} }
// TarSum is the generic interface for calculating fixed time // TarSum is the generic interface for calculating fixed time
// checksums of a tar archive // checksums of a tar archive.
type TarSum interface { type TarSum interface {
io.Reader io.Reader
GetSums() FileInfoSums GetSums() FileInfoSums
@ -75,7 +93,7 @@ type TarSum interface {
Hash() THash Hash() THash
} }
// tarSum struct is the structure for a Version0 checksum calculation // tarSum struct is the structure for a Version0 checksum calculation.
type tarSum struct { type tarSum struct {
io.Reader io.Reader
tarR *tar.Reader tarR *tar.Reader
@ -104,13 +122,13 @@ func (ts tarSum) Version() Version {
return ts.tarSumVersion return ts.tarSumVersion
} }
// A hash.Hash type generator and its name // THash provides a hash.Hash type generator and its name.
type THash interface { type THash interface {
Hash() hash.Hash Hash() hash.Hash
Name() string Name() string
} }
// Convenience method for creating a THash // NewTHash is a convenience method for creating a THash.
func NewTHash(name string, h func() hash.Hash) THash { func NewTHash(name string, h func() hash.Hash) THash {
return simpleTHash{n: name, h: h} return simpleTHash{n: name, h: h}
} }

View file

@ -8,7 +8,7 @@ import (
"strings" "strings"
) )
// versioning of the TarSum algorithm // Version is used for versioning of the TarSum algorithm
// based on the prefix of the hash used // based on the prefix of the hash used
// i.e. "tarsum+sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b" // i.e. "tarsum+sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b"
type Version int type Version int
@ -17,7 +17,7 @@ type Version int
const ( const (
Version0 Version = iota Version0 Version = iota
Version1 Version1
// NOTE: this variable will be either the latest or an unsettled next-version of the TarSum calculation // VersionDev this constant will be either the latest or an unsettled next-version of the TarSum calculation
VersionDev VersionDev
) )
@ -33,7 +33,7 @@ func VersionLabelForChecksum(checksum string) string {
return checksum[:sepIndex] return checksum[:sepIndex]
} }
// Get a list of all known tarsum Version // GetVersions gets a list of all known tarsum versions.
func GetVersions() []Version { func GetVersions() []Version {
v := []Version{} v := []Version{}
for k := range tarSumVersions { for k := range tarSumVersions {
@ -59,7 +59,7 @@ func (tsv Version) String() string {
return tarSumVersions[tsv] return tarSumVersions[tsv]
} }
// GetVersionFromTarsum returns the Version from the provided string // GetVersionFromTarsum returns the Version from the provided string.
func GetVersionFromTarsum(tarsum string) (Version, error) { func GetVersionFromTarsum(tarsum string) (Version, error) {
tsv := tarsum tsv := tarsum
if strings.Contains(tarsum, "+") { if strings.Contains(tarsum, "+") {

View file

@ -10,6 +10,9 @@ import (
// #include <termios.h> // #include <termios.h>
import "C" import "C"
// Termios is the Unix API for terminal I/O.
// It is passthgrouh for syscall.Termios in order to make it portable with
// other platforms where it is not available or handled differently.
type Termios syscall.Termios type Termios syscall.Termios
// MakeRaw put the terminal connected to the given file descriptor into raw // MakeRaw put the terminal connected to the given file descriptor into raw

View file

@ -1,5 +1,7 @@
// +build !windows // +build !windows
// Package term provides provides structures and helper functions to work with
// terminal (state, sizes).
package term package term
import ( import (
@ -12,13 +14,16 @@ import (
) )
var ( var (
// ErrInvalidState is returned if the state of the terminal is invalid.
ErrInvalidState = errors.New("Invalid terminal state") ErrInvalidState = errors.New("Invalid terminal state")
) )
// State represents the state of the terminal.
type State struct { type State struct {
termios Termios termios Termios
} }
// Winsize represents the size of the terminal window.
type Winsize struct { type Winsize struct {
Height uint16 Height uint16
Width uint16 Width uint16
@ -26,10 +31,12 @@ type Winsize struct {
y uint16 y uint16
} }
// StdStreams returns the standard streams (stdin, stdout, stedrr).
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
return os.Stdin, os.Stdout, os.Stderr return os.Stdin, os.Stdout, os.Stderr
} }
// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
func GetFdInfo(in interface{}) (uintptr, bool) { func GetFdInfo(in interface{}) (uintptr, bool) {
var inFd uintptr var inFd uintptr
var isTerminalIn bool var isTerminalIn bool
@ -40,6 +47,7 @@ func GetFdInfo(in interface{}) (uintptr, bool) {
return inFd, isTerminalIn return inFd, isTerminalIn
} }
// GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) { func GetWinsize(fd uintptr) (*Winsize, error) {
ws := &Winsize{} ws := &Winsize{}
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws))) _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
@ -50,6 +58,7 @@ func GetWinsize(fd uintptr) (*Winsize, error) {
return ws, err return ws, err
} }
// SetWinsize tries to set the specified window size for the specified file descriptor.
func SetWinsize(fd uintptr, ws *Winsize) error { func SetWinsize(fd uintptr, ws *Winsize) error {
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws))) _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
// Skipp errno = 0 // Skipp errno = 0
@ -65,8 +74,8 @@ func IsTerminal(fd uintptr) bool {
return tcget(fd, &termios) == 0 return tcget(fd, &termios) == 0
} }
// Restore restores the terminal connected to the given file descriptor to a // RestoreTerminal restores the terminal connected to the given file descriptor
// previous state. // to a previous state.
func RestoreTerminal(fd uintptr, state *State) error { func RestoreTerminal(fd uintptr, state *State) error {
if state == nil { if state == nil {
return ErrInvalidState return ErrInvalidState
@ -77,6 +86,7 @@ func RestoreTerminal(fd uintptr, state *State) error {
return nil return nil
} }
// SaveState saves the state of the terminal connected to the given file descriptor.
func SaveState(fd uintptr) (*State, error) { func SaveState(fd uintptr) (*State, error) {
var oldState State var oldState State
if err := tcget(fd, &oldState.termios); err != 0 { if err := tcget(fd, &oldState.termios); err != 0 {
@ -86,6 +96,8 @@ func SaveState(fd uintptr) (*State, error) {
return &oldState, nil return &oldState, nil
} }
// DisableEcho applies the specified state to the terminal connected to the file
// descriptor, with echo disabled.
func DisableEcho(fd uintptr, state *State) error { func DisableEcho(fd uintptr, state *State) error {
newState := state.termios newState := state.termios
newState.Lflag &^= syscall.ECHO newState.Lflag &^= syscall.ECHO
@ -97,6 +109,8 @@ func DisableEcho(fd uintptr, state *State) error {
return nil return nil
} }
// SetRawTerminal puts the terminal connected to the given file descriptor into
// raw mode and returns the previous state.
func SetRawTerminal(fd uintptr) (*State, error) { func SetRawTerminal(fd uintptr) (*State, error) {
oldState, err := MakeRaw(fd) oldState, err := MakeRaw(fd)
if err != nil { if err != nil {

View file

@ -23,6 +23,7 @@ type Winsize struct {
y uint16 y uint16
} }
// StdStreams returns the standard streams (stdin, stdout, stedrr).
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
switch { switch {
case os.Getenv("ConEmuANSI") == "ON": case os.Getenv("ConEmuANSI") == "ON":
@ -36,12 +37,12 @@ func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
} }
} }
// GetFdInfo returns file descriptor and bool indicating whether the file is a terminal. // GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
func GetFdInfo(in interface{}) (uintptr, bool) { func GetFdInfo(in interface{}) (uintptr, bool) {
return winconsole.GetHandleInfo(in) return winconsole.GetHandleInfo(in)
} }
// GetWinsize retrieves the window size of the terminal connected to the passed file descriptor. // GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) { func GetWinsize(fd uintptr) (*Winsize, error) {
info, err := winconsole.GetConsoleScreenBufferInfo(fd) info, err := winconsole.GetConsoleScreenBufferInfo(fd)
if err != nil { if err != nil {
@ -56,7 +57,7 @@ func GetWinsize(fd uintptr) (*Winsize, error) {
y: 0}, nil y: 0}, nil
} }
// SetWinsize sets the size of the given terminal connected to the passed file descriptor. // SetWinsize tries to set the specified window size for the specified file descriptor.
func SetWinsize(fd uintptr, ws *Winsize) error { func SetWinsize(fd uintptr, ws *Winsize) error {
// TODO(azlinux): Implement SetWinsize // TODO(azlinux): Implement SetWinsize
logrus.Debugf("[windows] SetWinsize: WARNING -- Unsupported method invoked") logrus.Debugf("[windows] SetWinsize: WARNING -- Unsupported method invoked")
@ -68,8 +69,8 @@ func IsTerminal(fd uintptr) bool {
return winconsole.IsConsole(fd) return winconsole.IsConsole(fd)
} }
// RestoreTerminal restores the terminal connected to the given file descriptor to a // RestoreTerminal restores the terminal connected to the given file descriptor
// previous state. // to a previous state.
func RestoreTerminal(fd uintptr, state *State) error { func RestoreTerminal(fd uintptr, state *State) error {
return winconsole.SetConsoleMode(fd, state.mode) return winconsole.SetConsoleMode(fd, state.mode)
} }
@ -94,8 +95,7 @@ func DisableEcho(fd uintptr, state *State) error {
} }
// SetRawTerminal puts the terminal connected to the given file descriptor into raw // 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 // mode and returns the previous state.
// restored.
func SetRawTerminal(fd uintptr) (*State, error) { func SetRawTerminal(fd uintptr) (*State, error) {
state, err := MakeRaw(fd) state, err := MakeRaw(fd)
if err != nil { if err != nil {

View file

@ -8,7 +8,10 @@ import (
const ( const (
getTermios = syscall.TIOCGETA getTermios = syscall.TIOCGETA
setTermios = syscall.TIOCSETA setTermios = syscall.TIOCSETA
)
// Termios magic numbers, passthrough to the ones defined in syscall.
const (
IGNBRK = syscall.IGNBRK IGNBRK = syscall.IGNBRK
PARMRK = syscall.PARMRK PARMRK = syscall.PARMRK
INLCR = syscall.INLCR INLCR = syscall.INLCR
@ -29,6 +32,7 @@ const (
IEXTEN = syscall.IEXTEN IEXTEN = syscall.IEXTEN
) )
// Termios is the Unix API for terminal I/O.
type Termios struct { type Termios struct {
Iflag uint64 Iflag uint64
Oflag uint64 Oflag uint64

View file

@ -8,7 +8,10 @@ import (
const ( const (
getTermios = syscall.TIOCGETA getTermios = syscall.TIOCGETA
setTermios = syscall.TIOCSETA setTermios = syscall.TIOCSETA
)
// Termios magic numbers, passthrough to the ones defined in syscall.
const (
IGNBRK = syscall.IGNBRK IGNBRK = syscall.IGNBRK
PARMRK = syscall.PARMRK PARMRK = syscall.PARMRK
INLCR = syscall.INLCR INLCR = syscall.INLCR
@ -29,6 +32,7 @@ const (
IEXTEN = syscall.IEXTEN IEXTEN = syscall.IEXTEN
) )
// Termios is the Unix API for terminal I/O.
type Termios struct { type Termios struct {
Iflag uint32 Iflag uint32
Oflag uint32 Oflag uint32

View file

@ -12,6 +12,7 @@ const (
setTermios = syscall.TCSETS setTermios = syscall.TCSETS
) )
// Termios is the Unix API for terminal I/O.
type Termios struct { type Termios struct {
Iflag uint32 Iflag uint32
Oflag uint32 Oflag uint32

View file

@ -1,3 +1,4 @@
// Package timeoutconn provides overridden net.Conn that supports deadline (timeout).
package timeoutconn package timeoutconn
import ( import (
@ -5,11 +6,13 @@ import (
"time" "time"
) )
// New creates a net.Conn with a timeout for every Read operation.
func New(netConn net.Conn, timeout time.Duration) net.Conn { func New(netConn net.Conn, timeout time.Duration) net.Conn {
return &conn{netConn, timeout} return &conn{netConn, timeout}
} }
// A net.Conn that sets a deadline for every Read or Write operation // A net.Conn that sets a deadline for every Read operation.
// FIXME was documented the deadline was on Write operation too but not implement
type conn struct { type conn struct {
net.Conn net.Conn
timeout time.Duration timeout time.Duration

View file

@ -1,3 +1,4 @@
// Package timeutils provides helper functions to parse and print time (time.Time).
package timeutils package timeutils
import ( import (

View file

@ -1,3 +1,6 @@
// Package truncindex package provides a general 'index tree', used by Docker
// in order to be able to reference containers by only a few unambiguous
// characters of their id.
package truncindex package truncindex
import ( import (
@ -10,7 +13,9 @@ import (
) )
var ( var (
ErrEmptyPrefix = errors.New("Prefix can't be empty") // ErrEmptyPrefix is an error returned if the prefix was empty.
ErrEmptyPrefix = errors.New("Prefix can't be empty")
// ErrAmbiguousPrefix is an error returned if the prefix was ambiguous (multiple ids for the prefix).
ErrAmbiguousPrefix = errors.New("Multiple IDs found with provided prefix") ErrAmbiguousPrefix = errors.New("Multiple IDs found with provided prefix")
) )
@ -22,7 +27,7 @@ type TruncIndex struct {
ids map[string]struct{} ids map[string]struct{}
} }
// NewTruncIndex creates a new TruncIndex and initializes with a list of IDs // NewTruncIndex creates a new TruncIndex and initializes with a list of IDs.
func NewTruncIndex(ids []string) (idx *TruncIndex) { func NewTruncIndex(ids []string) (idx *TruncIndex) {
idx = &TruncIndex{ idx = &TruncIndex{
ids: make(map[string]struct{}), ids: make(map[string]struct{}),
@ -54,7 +59,7 @@ func (idx *TruncIndex) addID(id string) error {
return nil return nil
} }
// Add adds a new ID to the TruncIndex // Add adds a new ID to the TruncIndex.
func (idx *TruncIndex) Add(id string) error { func (idx *TruncIndex) Add(id string) error {
idx.Lock() idx.Lock()
defer idx.Unlock() defer idx.Unlock()
@ -109,7 +114,7 @@ func (idx *TruncIndex) Get(s string) (string, error) {
return "", fmt.Errorf("no such id: %s", s) return "", fmt.Errorf("no such id: %s", s)
} }
// Iterates over all stored IDs, and passes each of them to the given handler // Iterate iterates over all stored IDs, and passes each of them to the given handler.
func (idx *TruncIndex) Iterate(handler func(id string)) { func (idx *TruncIndex) Iterate(handler func(id string)) {
idx.RLock() idx.RLock()
defer idx.RUnlock() defer idx.RUnlock()

View file

@ -1,3 +1,5 @@
// Package ulimit provides structure and helper function to parse and represent
// resource limits (Rlimit and Ulimit, its human friendly version).
package ulimit package ulimit
import ( import (
@ -6,13 +8,14 @@ import (
"strings" "strings"
) )
// Human friendly version of Rlimit // Ulimit is a human friendly version of Rlimit.
type Ulimit struct { type Ulimit struct {
Name string Name string
Hard int64 Hard int64
Soft int64 Soft int64
} }
// Rlimit specifies the resource limits, such as max open files.
type Rlimit struct { type Rlimit struct {
Type int `json:"type,omitempty"` Type int `json:"type,omitempty"`
Hard uint64 `json:"hard,omitempty"` Hard uint64 `json:"hard,omitempty"`
@ -24,43 +27,44 @@ const (
// some of these are defined in the syscall package, but not all. // some of these are defined in the syscall package, but not all.
// Also since Windows client doesn't get access to the syscall package, need to // Also since Windows client doesn't get access to the syscall package, need to
// define these here // define these here
RLIMIT_AS = 9 rlimitAs = 9
RLIMIT_CORE = 4 rlimitCore = 4
RLIMIT_CPU = 0 rlimitCPU = 0
RLIMIT_DATA = 2 rlimitData = 2
RLIMIT_FSIZE = 1 rlimitFsize = 1
RLIMIT_LOCKS = 10 rlimitLocks = 10
RLIMIT_MEMLOCK = 8 rlimitMemlock = 8
RLIMIT_MSGQUEUE = 12 rlimitMsgqueue = 12
RLIMIT_NICE = 13 rlimitNice = 13
RLIMIT_NOFILE = 7 rlimitNofile = 7
RLIMIT_NPROC = 6 rlimitNproc = 6
RLIMIT_RSS = 5 rlimitRss = 5
RLIMIT_RTPRIO = 14 rlimitRtprio = 14
RLIMIT_RTTIME = 15 rlimitRttime = 15
RLIMIT_SIGPENDING = 11 rlimitSigpending = 11
RLIMIT_STACK = 3 rlimitStack = 3
) )
var ulimitNameMapping = map[string]int{ var ulimitNameMapping = map[string]int{
//"as": RLIMIT_AS, // Disbaled since this doesn't seem usable with the way Docker inits a container. //"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container.
"core": RLIMIT_CORE, "core": rlimitCore,
"cpu": RLIMIT_CPU, "cpu": rlimitCPU,
"data": RLIMIT_DATA, "data": rlimitData,
"fsize": RLIMIT_FSIZE, "fsize": rlimitFsize,
"locks": RLIMIT_LOCKS, "locks": rlimitLocks,
"memlock": RLIMIT_MEMLOCK, "memlock": rlimitMemlock,
"msgqueue": RLIMIT_MSGQUEUE, "msgqueue": rlimitMsgqueue,
"nice": RLIMIT_NICE, "nice": rlimitNice,
"nofile": RLIMIT_NOFILE, "nofile": rlimitNofile,
"nproc": RLIMIT_NPROC, "nproc": rlimitNproc,
"rss": RLIMIT_RSS, "rss": rlimitRss,
"rtprio": RLIMIT_RTPRIO, "rtprio": rlimitRtprio,
"rttime": RLIMIT_RTTIME, "rttime": rlimitRttime,
"sigpending": RLIMIT_SIGPENDING, "sigpending": rlimitSigpending,
"stack": RLIMIT_STACK, "stack": rlimitStack,
} }
// Parse parses and returns a Ulimit from the specified string.
func Parse(val string) (*Ulimit, error) { func Parse(val string) (*Ulimit, error) {
parts := strings.SplitN(val, "=", 2) parts := strings.SplitN(val, "=", 2)
if len(parts) != 2 { if len(parts) != 2 {
@ -92,6 +96,7 @@ func Parse(val string) (*Ulimit, error) {
return &Ulimit{Name: parts[0], Soft: soft, Hard: hard}, nil return &Ulimit{Name: parts[0], Soft: soft, Hard: hard}, nil
} }
// GetRlimit returns the RLimit corresponding to Ulimit.
func (u *Ulimit) GetRlimit() (*Rlimit, error) { func (u *Ulimit) GetRlimit() (*Rlimit, error) {
t, exists := ulimitNameMapping[u.Name] t, exists := ulimitNameMapping[u.Name]
if !exists { if !exists {

View file

@ -1,3 +1,5 @@
// Package units provides helper function to parse and print size and time units
// in human-readable format.
package units package units
import ( import (
@ -6,7 +8,7 @@ import (
) )
// HumanDuration returns a human-readable approximation of a duration // HumanDuration returns a human-readable approximation of a duration
// (eg. "About a minute", "4 hours ago", etc.) // (eg. "About a minute", "4 hours ago", etc.).
func HumanDuration(d time.Duration) string { func HumanDuration(d time.Duration) string {
if seconds := int(d.Seconds()); seconds < 1 { if seconds := int(d.Seconds()); seconds < 1 {
return "Less than a second" return "Less than a second"

View file

@ -38,7 +38,7 @@ var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
// CustomSize returns a human-readable approximation of a size // CustomSize returns a human-readable approximation of a size
// using custom format // using custom format.
func CustomSize(format string, size float64, base float64, _map []string) string { func CustomSize(format string, size float64, base float64, _map []string) string {
i := 0 i := 0
for size >= base { for size >= base {
@ -49,17 +49,19 @@ func CustomSize(format string, size float64, base float64, _map []string) string
} }
// HumanSize returns a human-readable approximation of a size // HumanSize returns a human-readable approximation of a size
// using SI standard (eg. "44kB", "17MB") // using SI standard (eg. "44kB", "17MB").
func HumanSize(size float64) string { func HumanSize(size float64) string {
return CustomSize("%.4g %s", size, 1000.0, decimapAbbrs) return CustomSize("%.4g %s", size, 1000.0, decimapAbbrs)
} }
// BytesSize returns a human-readable size in bytes, kibibytes,
// mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB").
func BytesSize(size float64) string { func BytesSize(size float64) string {
return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs) return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs)
} }
// FromHumanSize returns an integer from a human-readable specification of a // FromHumanSize returns an integer from a human-readable specification of a
// size using SI standard (eg. "44kB", "17MB") // size using SI standard (eg. "44kB", "17MB").
func FromHumanSize(size string) (int64, error) { func FromHumanSize(size string) (int64, error) {
return parseSize(size, decimalMap) return parseSize(size, decimalMap)
} }
@ -72,7 +74,7 @@ func RAMInBytes(size string) (int64, error) {
return parseSize(size, binaryMap) return parseSize(size, binaryMap)
} }
// Parses the human-readable size string into the amount it represents // Parses the human-readable size string into the amount it represents.
func parseSize(sizeStr string, uMap unitMap) (int64, error) { func parseSize(sizeStr string, uMap unitMap) (int64, error) {
matches := sizeRegex.FindStringSubmatch(sizeStr) matches := sizeRegex.FindStringSubmatch(sizeStr)
if len(matches) != 3 { if len(matches) != 3 {

View file

@ -1,3 +1,5 @@
// Package urlutil provides helper function to check urls kind.
// It supports http urls, git urls and transport url (tcp://, …)
package urlutil package urlutil
import ( import (

View file

@ -3,14 +3,9 @@
package useragent package useragent
import ( import (
"errors"
"strings" "strings"
) )
var (
ErrNilRequest = errors.New("request cannot be nil")
)
// VersionInfo is used to model UserAgent versions. // VersionInfo is used to model UserAgent versions.
type VersionInfo struct { type VersionInfo struct {
Name string Name string
@ -30,7 +25,7 @@ func (vi *VersionInfo) isValid() bool {
return true return true
} }
// Convert versions to a string and append the string to the string base. // AppendVersions converts versions to a string and appends the string to the string base.
// //
// Each VersionInfo will be converted to a string in the format of // Each VersionInfo will be converted to a string in the format of
// "product/version", where the "product" is get from the name field, while // "product/version", where the "product" is get from the name field, while