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:
parent
a922d62168
commit
9bcb3cba83
56 changed files with 455 additions and 195 deletions
|
@ -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]
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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\\`),
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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{}
|
||||||
|
|
|
@ -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)
|
||||||
|
)
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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, "+") {
|
||||||
|
|
|
@ -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
|
||||||
|
|
18
term/term.go
18
term/term.go
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// Package timeutils provides helper functions to parse and print time (time.Time).
|
||||||
package timeutils
|
package timeutils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue