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 0852170e8c
commit 18c7c67308
61 changed files with 483 additions and 200 deletions

View File

@ -1555,7 +1555,7 @@ func (s *Server) initTcpSocket(addr string) (l net.Listener, err error) {
if s.cfg.TLSConfig == nil || s.cfg.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert {
logrus.Warn("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
}
if l, err = sockets.NewTcpSocket(addr, s.cfg.TLSConfig, s.start); err != nil {
if l, err = sockets.NewTCPSocket(addr, s.cfg.TLSConfig, s.start); err != nil {
return nil, err
}
if err := allocateDaemonPort(addr); err != nil {

View File

@ -182,11 +182,11 @@ func Build(d *daemon.Daemon, buildConfig *Config) error {
builder := &builder{
Daemon: d,
OutStream: &streamformatter.StdoutFormater{
OutStream: &streamformatter.StdoutFormatter{
Writer: buildConfig.Stdout,
StreamFormatter: sf,
},
ErrStream: &streamformatter.StderrFormater{
ErrStream: &streamformatter.StderrFormatter{
Writer: buildConfig.Stdout,
StreamFormatter: sf,
},

View File

@ -108,7 +108,7 @@ func checkKernel() error {
if k, err := kernel.GetKernelVersion(); err != nil {
logrus.Warnf("%s", err)
} else {
if kernel.CompareKernelVersion(k, &kernel.KernelVersionInfo{Kernel: 3, Major: 10, Minor: 0}) < 0 {
if kernel.CompareKernelVersion(*k, kernel.VersionInfo{Kernel: 3, Major: 10, Minor: 0}) < 0 {
if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" {
logrus.Warnf("You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.10.0.", k.String())
}

View File

@ -193,7 +193,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
}
}
var pfile *pidfile.PidFile
var pfile *pidfile.PIDFile
if cli.Pidfile != "" {
pf, err := pidfile.New(cli.Pidfile)
if err != nil {

View File

@ -28,14 +28,37 @@ packages=(
pkg/mount
pkg/namesgenerator
pkg/nat
pkg/parsers
pkg/parsers/filters
pkg/parsers/kernel
pkg/parsers/operatingsystem
pkg/pidfile
pkg/plugins
pkg/pools
pkg/progressreader
pkg/promise
pkg/proxy
pkg/pubsub
pkg/random
pkg/reexec
pkg/signal
pkg/sockets
pkg/streamformatter
pkg/stringid
pkg/stringutils
pkg/systemd
pkg/symlink
pkg/tailfile
pkg/tarsum
pkg/term
pkg/timeoutconn
pkg/timeutils
pkg/tlsconfig
pkg/truncindex
pkg/urlutil
pkg/ulimit
pkg/units
pkg/useragent
pkg/version
registry
utils

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
import (
@ -7,16 +9,22 @@ import (
"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
// 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*'`
//
// 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
filters := prev
if prev == nil {
filters = Args{}
}
@ -25,7 +33,7 @@ func ParseFlag(arg string, prev Args) (Args, error) {
}
if !strings.Contains(arg, "=") {
return filters, ErrorBadFormat
return filters, ErrBadFormat
}
f := strings.SplitN(arg, "=", 2)
@ -36,9 +44,10 @@ func ParseFlag(arg string, prev Args) (Args, error) {
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) {
// this way we don't URL encode {}, just empty space
if len(a) == 0 {
@ -52,7 +61,7 @@ func ToParam(a Args) (string, error) {
return string(buf), nil
}
// unpacks the filter Args
// FromParam unpacks the filter Args.
func FromParam(p string) (Args, error) {
args := Args{}
if len(p) == 0 {
@ -64,6 +73,11 @@ func FromParam(p string) (Args, error) {
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 {
fieldValues := filters[field]
@ -96,6 +110,10 @@ outer:
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 {
fieldValues := filters[field]

View File

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

View File

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

View File

@ -5,13 +5,13 @@ import (
"testing"
)
func assertParseRelease(t *testing.T, release string, b *KernelVersionInfo, result int) {
func assertParseRelease(t *testing.T, release string, b *VersionInfo, result int) {
var (
a *KernelVersionInfo
a *VersionInfo
)
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)
}
if a.Flavor != b.Flavor {
@ -20,13 +20,13 @@ func assertParseRelease(t *testing.T, release string, b *KernelVersionInfo, resu
}
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)
assertParseRelease(t, "3.8.0", &KernelVersionInfo{Kernel: 4, Major: 8, Minor: 0}, -1)
assertParseRelease(t, "3.8.0", &VersionInfo{Kernel: 3, Major: 8, Minor: 0}, 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", &VersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0)
assertParseRelease(t, "3.8.0-19-generic", &VersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "-19-generic"}, 0)
assertParseRelease(t, "3.12.8tag", &VersionInfo{Kernel: 3, Major: 12, Minor: 8, Flavor: "tag"}, 0)
assertParseRelease(t, "3.12-1-amd64", &VersionInfo{Kernel: 3, Major: 12, Minor: 0, Flavor: "-1-amd64"}, 0)
assertParseRelease(t, "3.8.0", &VersionInfo{Kernel: 4, Major: 8, Minor: 0}, -1)
// Errors
invalids := []string{
"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 {
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) {
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
VersionInfo{Kernel: 3, Major: 8, Minor: 0},
VersionInfo{Kernel: 3, Major: 8, Minor: 0},
0)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
VersionInfo{Kernel: 2, Major: 6, Minor: 0},
VersionInfo{Kernel: 3, Major: 8, Minor: 0},
-1)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
VersionInfo{Kernel: 3, Major: 8, Minor: 0},
VersionInfo{Kernel: 2, Major: 6, Minor: 0},
1)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
VersionInfo{Kernel: 3, Major: 8, Minor: 0},
VersionInfo{Kernel: 3, Major: 8, Minor: 0},
0)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
VersionInfo{Kernel: 3, Major: 8, Minor: 5},
VersionInfo{Kernel: 3, Major: 8, Minor: 0},
1)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
VersionInfo{Kernel: 3, Major: 0, Minor: 20},
VersionInfo{Kernel: 3, Major: 8, Minor: 0},
-1)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 7, Minor: 20},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
VersionInfo{Kernel: 3, Major: 7, Minor: 20},
VersionInfo{Kernel: 3, Major: 8, Minor: 0},
-1)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 20},
&KernelVersionInfo{Kernel: 3, Major: 7, Minor: 0},
VersionInfo{Kernel: 3, Major: 8, Minor: 20},
VersionInfo{Kernel: 3, Major: 7, Minor: 0},
1)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 20},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
VersionInfo{Kernel: 3, Major: 8, Minor: 20},
VersionInfo{Kernel: 3, Major: 8, Minor: 0},
1)
assertKernelVersion(t,
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 20},
VersionInfo{Kernel: 3, Major: 8, Minor: 0},
VersionInfo{Kernel: 3, Major: 8, Minor: 20},
-1)
}

View File

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

View File

@ -4,6 +4,9 @@ import (
"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
func uname() (*syscall.Utsname, error) {

View File

@ -6,6 +6,9 @@ import (
"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 {
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
import (
@ -14,6 +16,7 @@ var (
etcOsRelease = "/etc/os-release"
)
// GetOperatingSystem gets the name of the current operating system.
func GetOperatingSystem() (string, error) {
b, err := ioutil.ReadFile(etcOsRelease)
if err != nil {
@ -26,6 +29,7 @@ func GetOperatingSystem() (string, error) {
return "", errors.New("PRETTY_NAME not found")
}
// IsContainerized returns true if we are running inside a container.
func IsContainerized() (bool, error) {
b, err := ioutil.ReadFile(proc1Cgroup)
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
// for a similar sample
// GetOperatingSystem gets the name of the current operating system.
func GetOperatingSystem() (string, error) {
var h syscall.Handle
@ -41,7 +42,8 @@ func GetOperatingSystem() (string, error) {
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) {
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
import (
@ -9,6 +12,8 @@ import (
"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
func ParseHost(defaultTCPAddr, defaultUnixAddr, addr string) (string, error) {
addr = strings.TrimSpace(addr)
@ -17,7 +22,7 @@ func ParseHost(defaultTCPAddr, defaultUnixAddr, addr string) (string, error) {
addr = fmt.Sprintf("unix://%s", defaultUnixAddr)
} else {
// Note - defaultTCPAddr already includes tcp:// prefix
addr = fmt.Sprintf("%s", defaultTCPAddr)
addr = defaultTCPAddr
}
}
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) {
addr = strings.TrimPrefix(addr, "unix://")
if strings.Contains(addr, "://") {
@ -48,6 +57,9 @@ func ParseUnixAddr(addr string, defaultAddr string) (string, error) {
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) {
addr = strings.TrimPrefix(addr, "tcp://")
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
}
// 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.
// Ex: localhost.localdomain:5000/samalba/hipache:latest
// Digest ex: localhost:5000/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb
@ -94,6 +106,8 @@ func ParseRepositoryTag(repos string) (string, string) {
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) {
// ip:public:private
var (
@ -115,6 +129,7 @@ func PartParser(template, data string) (map[string]string, error) {
return out, nil
}
// ParseKeyValueOpt parses and validates the specified string as a key/value pair (key=value)
func ParseKeyValueOpt(opt string) (string, string, error) {
parts := strings.SplitN(opt, "=", 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
}
// ParsePortRange parses and validates the specified string as a port-range (8000-9000)
func ParsePortRange(ports string) (uint64, uint64, error) {
if 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
}
// ParseLink parses and validates the specified string as a link format (name:alias)
func ParseLink(val string) (string, string, error) {
if val == "" {
return "", "", fmt.Errorf("empty string specified for links")

View File

@ -7,7 +7,7 @@ import (
func TestParseHost(t *testing.T) {
var (
defaultHttpHost = "127.0.0.1"
defaultHTTPHost = "127.0.0.1"
defaultUnix = "/var/run/docker.sock"
)
invalids := map[string]string{
@ -32,12 +32,12 @@ func TestParseHost(t *testing.T) {
"fd://something": "fd://something",
}
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)
}
}
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)
}
}

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
import (
@ -8,11 +11,12 @@ import (
"strconv"
)
type PidFile struct {
// PIDFile is a file used to store the process ID of a running process.
type PIDFile struct {
path string
}
func checkPidFileAlreadyExists(path string) error {
func checkPIDFileAlreadyExists(path string) error {
if pidString, err := ioutil.ReadFile(path); err == nil {
if pid, err := strconv.Atoi(string(pidString)); err == nil {
if _, err := os.Stat(filepath.Join("/proc", string(pid))); err == nil {
@ -23,18 +27,20 @@ func checkPidFileAlreadyExists(path string) error {
return nil
}
func New(path string) (*PidFile, error) {
if err := checkPidFileAlreadyExists(path); err != nil {
// New creates a PIDfile using the specified path.
func New(path string) (*PIDFile, error) {
if err := checkPIDFileAlreadyExists(path); err != nil {
return nil, err
}
if err := ioutil.WriteFile(path, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil {
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 {
return err
}

View File

@ -24,7 +24,7 @@ func TestNewAndRemove(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 {
t.Fatal("Non-existing file doesn't give an error on delete")

View File

@ -19,6 +19,7 @@ const (
defaultTimeOut = 30
)
// NewClient creates a new plugin client (http).
func NewClient(addr string, tlsConfig tlsconfig.Options) (*Client, error) {
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
}
// Client represents a plugin client.
type Client struct {
http *http.Client
addr string
http *http.Client // http client to use
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 {
return c.callWithRetry(serviceMethod, args, ret, true)
}

View File

@ -12,22 +12,28 @@ import (
)
var (
// ErrNotFound plugin not found
ErrNotFound = errors.New("Plugin not found")
socketsPath = "/run/docker/plugins"
specsPaths = []string{"/etc/docker/plugins", "/usr/lib/docker/plugins"}
)
// Registry defines behavior of a registry of plugins.
type Registry interface {
// Plugins lists all plugins.
Plugins() ([]*Plugin, error)
// Plugin returns the plugin registered with the given name (or returns an error).
Plugin(name string) (*Plugin, error)
}
// LocalRegistry defines a registry that is local (using unix socket).
type LocalRegistry struct{}
func newLocalRegistry() LocalRegistry {
return LocalRegistry{}
}
// Plugin returns the plugin registered with the given name (or returns an error).
func (l *LocalRegistry) Plugin(name string) (*Plugin, error) {
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
import (
@ -9,6 +31,7 @@ import (
)
var (
// ErrNotImplements is returned if the 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))
)
// Manifest lists what a plugin implements.
type Manifest struct {
// List of subsystem the plugin implements.
Implements []string
}
// Plugin is the definition of a docker plugin.
type Plugin struct {
Name string `json:"-"`
Addr string
// Name of the plugin
Name string `json:"-"`
// Address of the plugin
Addr string
// TLS configuration of the plugin
TLSConfig tlsconfig.Options
Client *Client `json:"-"`
Manifest *Manifest `json:"-"`
// Client attached to the plugin
Client *Client `json:"-"`
// Manifest of the plugin (see above)
Manifest *Manifest `json:"-"`
}
func newLocalPlugin(name, addr string) *Plugin {
@ -96,6 +127,7 @@ func get(name string) (*Plugin, error) {
return pl, nil
}
// Get returns the plugin given the specified name and requested implementation.
func Get(name, imp string) (*Plugin, error) {
pl, err := get(name)
if err != nil {
@ -110,6 +142,7 @@ func Get(name, imp string) (*Plugin, error) {
return nil, ErrNotImplements
}
// Handle adds the specified function to the extpointHandlers.
func Handle(iface string, fn func(string, *Client)) {
extpointHandlers[iface] = fn
}

View File

@ -18,14 +18,15 @@ import (
)
var (
// Pool which returns bufio.Reader with a 32K buffer
// BufioReader32KPool is a pool which returns bufio.Reader with a 32K buffer.
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
)
const buffer32K = 32 * 1024
// BufioReaderPool is a bufio reader that uses sync.Pool.
type BufioReaderPool struct {
pool sync.Pool
}
@ -57,7 +58,7 @@ func (bufPool *BufioReaderPool) Put(b *bufio.Reader) {
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) {
buf := BufioReader32KPool.Get(src)
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 {
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
import (
@ -7,7 +9,7 @@ import (
"github.com/docker/docker/pkg/streamformatter"
)
// Reader with progress bar
// Config contains the configuration for a Reader with progress bar.
type Config struct {
In io.ReadCloser // Stream to read from
Out io.Writer // Where to send progress bar to
@ -20,6 +22,7 @@ type Config struct {
Action string
}
// New creates a new Config.
func New(newReader Config) *Config {
return &newReader
}
@ -48,6 +51,7 @@ func (config *Config) Read(p []byte) (n int, err error) {
return read, err
}
// Close closes the reader (Config).
func (config *Config) Close() error {
if config.Current < config.Size {
//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
import (
@ -5,18 +7,24 @@ import (
"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 {
// Start forwarding traffic back and forth the front and back-end
// addresses.
// Run starts forwarding traffic back and forth between the front
// and back-end addresses.
Run()
// Stop forwarding traffic and close both ends of the Proxy.
// Close stops forwarding traffic and close both ends of the Proxy.
Close()
// Return the address on which the proxy is listening.
// FrontendAddr returns the address on which the proxy is listening.
FrontendAddr() net.Addr
// Return the proxied address.
// BackendAddr returns the proxied address.
BackendAddr() net.Addr
}
// NewProxy creates a Proxy according to the specified frontendAddr and backendAddr.
func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
switch frontendAddr.(type) {
case *net.UDPAddr:

View File

@ -4,16 +4,25 @@ import (
"net"
)
// StubProxy is a proxy that is a stub (does nothing).
type StubProxy struct {
frontendAddr net.Addr
backendAddr net.Addr
}
func (p *StubProxy) Run() {}
func (p *StubProxy) Close() {}
func (p *StubProxy) FrontendAddr() net.Addr { return p.frontendAddr }
func (p *StubProxy) BackendAddr() net.Addr { return p.backendAddr }
// Run does nothing.
func (p *StubProxy) Run() {}
// 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) {
return &StubProxy{
frontendAddr: frontendAddr,

View File

@ -8,12 +8,15 @@ import (
"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 {
listener *net.TCPListener
frontendAddr *net.TCPAddr
backendAddr *net.TCPAddr
}
// NewTCPProxy creates a new TCPProxy.
func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
listener, err := net.ListenTCP("tcp", frontendAddr)
if err != nil {
@ -53,7 +56,7 @@ func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
go broker(client, backend)
go broker(backend, client)
var transferred int64 = 0
var transferred int64
for i := 0; i < 2; i++ {
select {
case written := <-event:
@ -72,6 +75,7 @@ func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
backend.Close()
}
// Run starts forwarding the traffic using TCP.
func (proxy *TCPProxy) Run() {
quit := make(chan bool)
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) 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 (
// UDPConnTrackTimeout is the timeout used for UDP connection tracking
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
@ -41,6 +43,9 @@ func newConnTrackKey(addr *net.UDPAddr) *connTrackKey {
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 {
listener *net.UDPConn
frontendAddr *net.UDPAddr
@ -49,6 +54,7 @@ type UDPProxy struct {
connTrackLock sync.Mutex
}
// NewUDPProxy creates a new UDPProxy.
func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) {
listener, err := net.ListenUDP("udp", frontendAddr)
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() {
readBuf := make([]byte, UDPBufSize)
for {
@ -135,6 +142,7 @@ func (proxy *UDPProxy) Run() {
}
}
// Close stops forwarding the traffic.
func (proxy *UDPProxy) Close() {
proxy.listener.Close()
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) 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 {
/* 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
import (
@ -5,6 +7,7 @@ import (
"os/signal"
)
// CatchAll catches all signals and relays them to the specified channel.
func CatchAll(sigc chan os.Signal) {
handledSigs := []os.Signal{}
for _, s := range SignalMap {
@ -13,6 +16,7 @@ func CatchAll(sigc chan os.Signal) {
signal.Notify(sigc, handledSigs...)
}
// StopCatch stops catching the signals and closes the specified channel.
func StopCatch(sigc chan os.Signal) {
signal.Stop(sigc)
close(sigc)

View File

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

View File

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

View File

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

View File

@ -8,5 +8,9 @@ import (
// Signals used in api/client (no windows equivalent, use
// 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
// SIGWINCH is a signal sent to a process when its controlling terminal changes its size
const SIGWINCH = syscall.SIGWINCH

View File

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

View File

@ -8,5 +8,7 @@ import (
// Signals used in api/client (no windows equivalent, use
// invalid signals so they don't get handled)
const SIGCHLD = syscall.Signal(0xff)
const SIGWINCH = syscall.Signal(0xff)
const (
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() {
buf := make([]byte, 16384)
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
import (
@ -9,7 +11,12 @@ import (
"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)
if err != nil {
return nil, err
@ -21,6 +28,10 @@ func NewTcpSocket(addr string, tlsConfig *tls.Config, activate <-chan struct{})
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) {
// Why 32? See https://github.com/docker/docker/pull/8035.
timeout := 32 * time.Second

View File

@ -14,6 +14,9 @@ import (
"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) {
if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) {
return nil, err

View File

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

View File

@ -1,3 +1,4 @@
// Package stringid provides helper functions for dealing with string identifiers
package stringid
import (
@ -12,7 +13,7 @@ const shortLen = 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 {
return validShortID.MatchString(id)
}
@ -29,7 +30,7 @@ func TruncateID(id string) string {
return id[:trimTo]
}
// GenerateRandomID returns an unique id
// GenerateRandomID returns an unique id.
func GenerateRandomID() string {
for {
id := make([]byte, 32)

View File

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

View File

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

View File

@ -6,9 +6,10 @@ import (
"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 {
socketAddr := &net.UnixAddr{
Name: os.Getenv("NOTIFY_SOCKET"),
@ -16,7 +17,7 @@ func SdNotify(state string) error {
}
if socketAddr.Name == "" {
return SdNotifyNoSocket
return ErrSdNotifyNoSocket
}
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
import (
@ -10,9 +12,11 @@ import (
const blockSize = 1024
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) {
if n <= 0 {
return nil, ErrNonPositiveLinesNumber

View File

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

View File

@ -2,7 +2,9 @@ package tarsum
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 {
// File name
Name() string
@ -28,9 +30,10 @@ func (fis fileInfoSum) Pos() int64 {
return fis.pos
}
// FileInfoSums provides a list of FileInfoSumInterfaces.
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 {
for i := range fis {
if fis[i].Name() == name {
@ -40,7 +43,7 @@ func (fis FileInfoSums) GetFile(name string) FileInfoSumInterface {
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 {
f := FileInfoSums{}
for i := range fis {
@ -51,6 +54,7 @@ func (fis FileInfoSums) GetAllFile(name string) FileInfoSums {
return f
}
// GetDuplicatePaths returns a FileInfoSums with all duplicated paths.
func (fis FileInfoSums) GetDuplicatePaths() (dups FileInfoSums) {
seen := make(map[string]int, len(fis)) // allocate earl. no need to grow this map.
for i := range fis {
@ -64,17 +68,23 @@ func (fis FileInfoSums) GetDuplicatePaths() (dups FileInfoSums) {
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] }
// SortByPos sorts FileInfoSums content by position.
func (fis FileInfoSums) SortByPos() {
sort.Sort(byPos{fis})
}
// SortByNames sorts FileInfoSums content by name.
func (fis FileInfoSums) SortByNames() {
sort.Sort(byName{fis})
}
// SortBySums sorts FileInfoSums content by sums.
func (fis FileInfoSums) SortBySums() {
dups := fis.GetDuplicatePaths()
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
import (
@ -30,7 +47,8 @@ func NewTarSum(r io.Reader, dc bool, v Version) (TarSum, error) {
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) {
headerSelector, err := getTarHeaderSelector(v)
if err != nil {
@ -41,7 +59,7 @@ func NewTarSumHash(r io.Reader, dc bool, v Version, tHash THash) (TarSum, error)
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) {
parts := strings.SplitN(label, "+", 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
// checksums of a tar archive
// checksums of a tar archive.
type TarSum interface {
io.Reader
GetSums() FileInfoSums
@ -75,7 +93,7 @@ type TarSum interface {
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 {
io.Reader
tarR *tar.Reader
@ -104,13 +122,13 @@ func (ts tarSum) Version() Version {
return ts.tarSumVersion
}
// A hash.Hash type generator and its name
// THash provides a hash.Hash type generator and its name.
type THash interface {
Hash() hash.Hash
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 {
return simpleTHash{n: name, h: h}
}

View File

@ -8,7 +8,7 @@ import (
"strings"
)
// versioning of the TarSum algorithm
// Version is used for versioning of the TarSum algorithm
// based on the prefix of the hash used
// i.e. "tarsum+sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b"
type Version int
@ -17,7 +17,7 @@ type Version int
const (
Version0 Version = iota
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
)
@ -33,7 +33,7 @@ func VersionLabelForChecksum(checksum string) string {
return checksum[:sepIndex]
}
// Get a list of all known tarsum Version
// GetVersions gets a list of all known tarsum versions.
func GetVersions() []Version {
v := []Version{}
for k := range tarSumVersions {
@ -59,7 +59,7 @@ func (tsv Version) String() string {
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) {
tsv := tarsum
if strings.Contains(tarsum, "+") {

View File

@ -10,6 +10,9 @@ import (
// #include <termios.h>
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
// MakeRaw put the terminal connected to the given file descriptor into raw

View File

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

View File

@ -23,6 +23,7 @@ type Winsize struct {
y uint16
}
// StdStreams returns the standard streams (stdin, stdout, stedrr).
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
switch {
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) {
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) {
info, err := winconsole.GetConsoleScreenBufferInfo(fd)
if err != nil {
@ -56,7 +57,7 @@ func GetWinsize(fd uintptr) (*Winsize, error) {
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 {
// TODO(azlinux): Implement SetWinsize
logrus.Debugf("[windows] SetWinsize: WARNING -- Unsupported method invoked")
@ -68,8 +69,8 @@ func IsTerminal(fd uintptr) bool {
return winconsole.IsConsole(fd)
}
// RestoreTerminal restores the terminal connected to the given file descriptor to a
// previous state.
// RestoreTerminal restores the terminal connected to the given file descriptor
// to a previous state.
func RestoreTerminal(fd uintptr, state *State) error {
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
// mode and returns the previous state of the terminal so that it can be
// restored.
// mode and returns the previous state.
func SetRawTerminal(fd uintptr) (*State, error) {
state, err := MakeRaw(fd)
if err != nil {

View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
// Package timeoutconn provides overridden net.Conn that supports deadline (timeout).
package timeoutconn
import (
@ -5,11 +6,13 @@ import (
"time"
)
// New creates a net.Conn with a timeout for every Read operation.
func New(netConn net.Conn, timeout time.Duration) net.Conn {
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 {
net.Conn
timeout time.Duration

View File

@ -1,3 +1,4 @@
// Package timeutils provides helper functions to parse and print time (time.Time).
package timeutils
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
import (
@ -10,7 +13,9 @@ import (
)
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")
)
@ -22,7 +27,7 @@ type TruncIndex 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) {
idx = &TruncIndex{
ids: make(map[string]struct{}),
@ -54,7 +59,7 @@ func (idx *TruncIndex) addID(id string) error {
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 {
idx.Lock()
defer idx.Unlock()
@ -109,7 +114,7 @@ func (idx *TruncIndex) Get(s string) (string, error) {
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)) {
idx.RLock()
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
import (
@ -6,13 +8,14 @@ import (
"strings"
)
// Human friendly version of Rlimit
// Ulimit is a human friendly version of Rlimit.
type Ulimit struct {
Name string
Hard int64
Soft int64
}
// Rlimit specifies the resource limits, such as max open files.
type Rlimit struct {
Type int `json:"type,omitempty"`
Hard uint64 `json:"hard,omitempty"`
@ -24,43 +27,44 @@ const (
// 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
// define these here
RLIMIT_AS = 9
RLIMIT_CORE = 4
RLIMIT_CPU = 0
RLIMIT_DATA = 2
RLIMIT_FSIZE = 1
RLIMIT_LOCKS = 10
RLIMIT_MEMLOCK = 8
RLIMIT_MSGQUEUE = 12
RLIMIT_NICE = 13
RLIMIT_NOFILE = 7
RLIMIT_NPROC = 6
RLIMIT_RSS = 5
RLIMIT_RTPRIO = 14
RLIMIT_RTTIME = 15
RLIMIT_SIGPENDING = 11
RLIMIT_STACK = 3
rlimitAs = 9
rlimitCore = 4
rlimitCPU = 0
rlimitData = 2
rlimitFsize = 1
rlimitLocks = 10
rlimitMemlock = 8
rlimitMsgqueue = 12
rlimitNice = 13
rlimitNofile = 7
rlimitNproc = 6
rlimitRss = 5
rlimitRtprio = 14
rlimitRttime = 15
rlimitSigpending = 11
rlimitStack = 3
)
var ulimitNameMapping = map[string]int{
//"as": RLIMIT_AS, // Disbaled since this doesn't seem usable with the way Docker inits a container.
"core": RLIMIT_CORE,
"cpu": RLIMIT_CPU,
"data": RLIMIT_DATA,
"fsize": RLIMIT_FSIZE,
"locks": RLIMIT_LOCKS,
"memlock": RLIMIT_MEMLOCK,
"msgqueue": RLIMIT_MSGQUEUE,
"nice": RLIMIT_NICE,
"nofile": RLIMIT_NOFILE,
"nproc": RLIMIT_NPROC,
"rss": RLIMIT_RSS,
"rtprio": RLIMIT_RTPRIO,
"rttime": RLIMIT_RTTIME,
"sigpending": RLIMIT_SIGPENDING,
"stack": RLIMIT_STACK,
//"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container.
"core": rlimitCore,
"cpu": rlimitCPU,
"data": rlimitData,
"fsize": rlimitFsize,
"locks": rlimitLocks,
"memlock": rlimitMemlock,
"msgqueue": rlimitMsgqueue,
"nice": rlimitNice,
"nofile": rlimitNofile,
"nproc": rlimitNproc,
"rss": rlimitRss,
"rtprio": rlimitRtprio,
"rttime": rlimitRttime,
"sigpending": rlimitSigpending,
"stack": rlimitStack,
}
// Parse parses and returns a Ulimit from the specified string.
func Parse(val string) (*Ulimit, error) {
parts := strings.SplitN(val, "=", 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
}
// GetRlimit returns the RLimit corresponding to Ulimit.
func (u *Ulimit) GetRlimit() (*Rlimit, error) {
t, exists := ulimitNameMapping[u.Name]
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
import (
@ -6,7 +8,7 @@ import (
)
// 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 {
if seconds := int(d.Seconds()); seconds < 1 {
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"}
// 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 {
i := 0
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
// using SI standard (eg. "44kB", "17MB")
// using SI standard (eg. "44kB", "17MB").
func HumanSize(size float64) string {
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 {
return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs)
}
// 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) {
return parseSize(size, decimalMap)
}
@ -72,7 +74,7 @@ func RAMInBytes(size string) (int64, error) {
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) {
matches := sizeRegex.FindStringSubmatch(sizeStr)
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
import (

View File

@ -3,14 +3,9 @@
package useragent
import (
"errors"
"strings"
)
var (
ErrNilRequest = errors.New("request cannot be nil")
)
// VersionInfo is used to model UserAgent versions.
type VersionInfo struct {
Name string
@ -30,7 +25,7 @@ func (vi *VersionInfo) isValid() bool {
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
// "product/version", where the "product" is get from the name field, while