Merge pull request #195 from ibuildthecloud/rootless

rootless
pull/357/head v0.4.0-rc1
Darren Shepherd 2019-04-09 10:50:17 -07:00 committed by GitHub
commit b5217e2888
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
233 changed files with 8935 additions and 2345 deletions

View File

@ -15,6 +15,7 @@ import (
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/natefinch/lumberjack" "github.com/natefinch/lumberjack"
"github.com/opencontainers/runc/libcontainer/system"
util2 "github.com/rancher/k3s/pkg/agent/util" util2 "github.com/rancher/k3s/pkg/agent/util"
"github.com/rancher/k3s/pkg/daemons/config" "github.com/rancher/k3s/pkg/daemons/config"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -27,10 +28,15 @@ const (
maxMsgSize = 1024 * 1024 * 16 maxMsgSize = 1024 * 1024 * 16
configToml = ` configToml = `
[plugins.opt] [plugins.opt]
path = "%OPT%" path = "%OPT%"
[plugins.cri] [plugins.cri]
stream_server_address = "%NODE%" stream_server_address = "%NODE%"
stream_server_port = "10010" stream_server_port = "10010"
`
configUserNSToml = `
disable_cgroup = true
disable_apparmor = true
restrict_oom_score_adj = true
` `
configCNIToml = ` configCNIToml = `
[plugins.cri.cni] [plugins.cri.cni]
@ -49,6 +55,9 @@ func Run(ctx context.Context, cfg *config.Node) error {
} }
template := configToml template := configToml
if system.RunningInUserNS() {
template += configUserNSToml
}
if !cfg.NoFlannel { if !cfg.NoFlannel {
template += configCNIToml template += configCNIToml
} }

View File

@ -17,6 +17,7 @@ import (
"github.com/rancher/k3s/pkg/agent/tunnel" "github.com/rancher/k3s/pkg/agent/tunnel"
"github.com/rancher/k3s/pkg/cli/cmds" "github.com/rancher/k3s/pkg/cli/cmds"
"github.com/rancher/k3s/pkg/daemons/agent" "github.com/rancher/k3s/pkg/daemons/agent"
"github.com/rancher/k3s/pkg/rootless"
"github.com/rancher/norman/pkg/clientaccess" "github.com/rancher/norman/pkg/clientaccess"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -69,6 +70,12 @@ func Run(ctx context.Context, cfg cmds.Agent) error {
return err return err
} }
if cfg.Rootless {
if err := rootless.Rootless(cfg.DataDir); err != nil {
return err
}
}
cfg.DataDir = filepath.Join(cfg.DataDir, "agent") cfg.DataDir = filepath.Join(cfg.DataDir, "agent")
if cfg.ClusterSecret != "" { if cfg.ClusterSecret != "" {

View File

@ -10,7 +10,7 @@ import (
"github.com/rancher/k3s/pkg/agent" "github.com/rancher/k3s/pkg/agent"
"github.com/rancher/k3s/pkg/cli/cmds" "github.com/rancher/k3s/pkg/cli/cmds"
"github.com/rancher/norman/pkg/resolvehome" "github.com/rancher/k3s/pkg/datadir"
"github.com/rancher/norman/signal" "github.com/rancher/norman/signal"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -57,7 +57,7 @@ func Run(ctx *cli.Context) error {
logrus.Infof("Starting k3s agent %s", ctx.App.Version) logrus.Infof("Starting k3s agent %s", ctx.App.Version)
dataDir, err := resolvehome.Resolve(cmds.AgentConfig.DataDir) dataDir, err := datadir.LocalHome(cmds.AgentConfig.DataDir, cmds.AgentConfig.Rootless)
if err != nil { if err != nil {
return err return err
} }

View File

@ -20,6 +20,7 @@ type Agent struct {
ContainerRuntimeEndpoint string ContainerRuntimeEndpoint string
NoFlannel bool NoFlannel bool
Debug bool Debug bool
Rootless bool
AgentShared AgentShared
ExtraKubeletArgs cli.StringSlice ExtraKubeletArgs cli.StringSlice
ExtraKubeProxyArgs cli.StringSlice ExtraKubeProxyArgs cli.StringSlice
@ -113,6 +114,11 @@ func NewAgentCommand(action func(ctx *cli.Context) error) cli.Command {
Destination: &AgentConfig.ClusterSecret, Destination: &AgentConfig.ClusterSecret,
EnvVar: "K3S_CLUSTER_SECRET", EnvVar: "K3S_CLUSTER_SECRET",
}, },
cli.BoolFlag{
Name: "rootless",
Usage: "(experimental) Run rootless",
Destination: &AgentConfig.Rootless,
},
DockerFlag, DockerFlag,
FlannelFlag, FlannelFlag,
NodeNameFlag, NodeNameFlag,

View File

@ -21,6 +21,7 @@ type Server struct {
ExtraAPIArgs cli.StringSlice ExtraAPIArgs cli.StringSlice
ExtraSchedulerArgs cli.StringSlice ExtraSchedulerArgs cli.StringSlice
ExtraControllerArgs cli.StringSlice ExtraControllerArgs cli.StringSlice
Rootless bool
} }
var ServerConfig Server var ServerConfig Server
@ -124,6 +125,11 @@ func NewServerCommand(action func(*cli.Context) error) cli.Command {
Usage: "Customized flag for kube-controller-manager process", Usage: "Customized flag for kube-controller-manager process",
Value: &ServerConfig.ExtraControllerArgs, Value: &ServerConfig.ExtraControllerArgs,
}, },
cli.BoolFlag{
Name: "rootless",
Usage: "(experimental) Run rootless",
Destination: &ServerConfig.Rootless,
},
NodeIPFlag, NodeIPFlag,
NodeNameFlag, NodeNameFlag,
DockerFlag, DockerFlag,

View File

@ -15,12 +15,14 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rancher/k3s/pkg/agent" "github.com/rancher/k3s/pkg/agent"
"github.com/rancher/k3s/pkg/cli/cmds" "github.com/rancher/k3s/pkg/cli/cmds"
"github.com/rancher/k3s/pkg/datadir"
"github.com/rancher/k3s/pkg/rootless"
"github.com/rancher/k3s/pkg/server" "github.com/rancher/k3s/pkg/server"
"github.com/rancher/norman/signal" "github.com/rancher/norman/signal"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
"k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/net"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume/csi"
_ "github.com/mattn/go-sqlite3" // ensure we have sqlite _ "github.com/mattn/go-sqlite3" // ensure we have sqlite
) )
@ -67,18 +69,30 @@ func run(app *cli.Context, cfg *cmds.Server) error {
setupLogging(app) setupLogging(app)
if !cfg.DisableAgent && os.Getuid() != 0 { if !cfg.DisableAgent && os.Getuid() != 0 && !cfg.Rootless {
return fmt.Errorf("must run as root unless --disable-agent is specified") return fmt.Errorf("must run as root unless --disable-agent is specified")
} }
if cfg.Rootless {
dataDir, err := datadir.LocalHome(cfg.DataDir, true)
if err != nil {
return err
}
cfg.DataDir = dataDir
if err := rootless.Rootless(dataDir); err != nil {
return err
}
}
// If running agent in server, set this so that CSI initializes properly // If running agent in server, set this so that CSI initializes properly
volume.WaitForValidHost = !cfg.DisableAgent csi.WaitForValidHostName = !cfg.DisableAgent
serverConfig := server.Config{} serverConfig := server.Config{}
serverConfig.ControlConfig.ClusterSecret = cfg.ClusterSecret serverConfig.ControlConfig.ClusterSecret = cfg.ClusterSecret
serverConfig.ControlConfig.DataDir = cfg.DataDir serverConfig.ControlConfig.DataDir = cfg.DataDir
serverConfig.ControlConfig.KubeConfigOutput = cfg.KubeConfigOutput serverConfig.ControlConfig.KubeConfigOutput = cfg.KubeConfigOutput
serverConfig.ControlConfig.KubeConfigMode = cfg.KubeConfigMode serverConfig.ControlConfig.KubeConfigMode = cfg.KubeConfigMode
serverConfig.Rootless = cfg.Rootless
serverConfig.TLSConfig.HTTPSPort = cfg.HTTPSPort serverConfig.TLSConfig.HTTPSPort = cfg.HTTPSPort
serverConfig.TLSConfig.HTTPPort = cfg.HTTPPort serverConfig.TLSConfig.HTTPPort = cfg.HTTPPort
serverConfig.TLSConfig.KnownIPs = knownIPs(cfg.KnownIPs) serverConfig.TLSConfig.KnownIPs = knownIPs(cfg.KnownIPs)

View File

@ -9,12 +9,14 @@ import (
"strings" "strings"
"time" "time"
"github.com/opencontainers/runc/libcontainer/system"
"github.com/rancher/k3s/pkg/daemons/config" "github.com/rancher/k3s/pkg/daemons/config"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/net"
"k8s.io/component-base/logs" "k8s.io/component-base/logs"
app2 "k8s.io/kubernetes/cmd/kube-proxy/app" app2 "k8s.io/kubernetes/cmd/kube-proxy/app"
"k8s.io/kubernetes/cmd/kubelet/app" "k8s.io/kubernetes/cmd/kubelet/app"
_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration _ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
_ "k8s.io/kubernetes/pkg/version/prometheus" // for version metric registration _ "k8s.io/kubernetes/pkg/version/prometheus" // for version metric registration
) )
@ -107,8 +109,11 @@ func kubelet(cfg *config.Agent) {
argsMap["runtime-cgroups"] = root argsMap["runtime-cgroups"] = root
argsMap["kubelet-cgroups"] = root argsMap["kubelet-cgroups"] = root
} }
args := config.GetArgsList(argsMap, cfg.ExtraKubeletArgs) if system.RunningInUserNS() {
argsMap["feature-gates"] = "DevicePlugins=false"
}
args := config.GetArgsList(argsMap, cfg.ExtraKubeletArgs)
command.SetArgs(args) command.SetArgs(args)
go func() { go func() {

View File

@ -15,8 +15,12 @@ const (
) )
func Resolve(dataDir string) (string, error) { func Resolve(dataDir string) (string, error) {
return LocalHome(dataDir, false)
}
func LocalHome(dataDir string, forceLocal bool) (string, error) {
if dataDir == "" { if dataDir == "" {
if os.Getuid() == 0 { if os.Getuid() == 0 && !forceLocal {
dataDir = DefaultDataDir dataDir = DefaultDataDir
} else { } else {
dataDir = DefaultHomeDataDir dataDir = DefaultHomeDataDir

View File

@ -17,7 +17,7 @@ import (
func Main() { func Main() {
kubenv := os.Getenv("KUBECONFIG") kubenv := os.Getenv("KUBECONFIG")
if kubenv == "" { if kubenv == "" {
config, err := server.HomeKubeConfig(false) config, err := server.HomeKubeConfig(false, false)
if _, serr := os.Stat(config); err == nil && serr == nil { if _, serr := os.Stat(config); err == nil && serr == nil {
os.Setenv("KUBECONFIG", config) os.Setenv("KUBECONFIG", config)
} }

64
pkg/rootless/mounts.go Normal file
View File

@ -0,0 +1,64 @@
package rootless
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
func setupMounts(stateDir string) error {
mountMap := [][]string{
{"/run", ""},
{"/var/run", ""},
{"/var/log", filepath.Join(stateDir, "logs")},
{"/var/lib/cni", filepath.Join(stateDir, "cni")},
}
for _, v := range mountMap {
if err := setupMount(v[0], v[1]); err != nil {
return errors.Wrapf(err, "failed to setup mount %s => %s", v[0], v[1])
}
}
return nil
}
func setupMount(target, dir string) error {
toCreate := target
for {
if toCreate == "/" {
return fmt.Errorf("missing /%s on the root filesystem", strings.Split(target, "/")[0])
}
if err := os.MkdirAll(toCreate, 0700); err == nil {
break
}
toCreate = filepath.Base(toCreate)
}
logrus.Debug("Mounting none ", toCreate, " tmpfs")
if err := unix.Mount("none", toCreate, "tmpfs", 0, ""); err != nil {
return errors.Wrapf(err, "failed to mount tmpfs to %s", toCreate)
}
if err := os.MkdirAll(target, 0700); err != nil {
return errors.Wrapf(err, "failed to create directory %s")
}
if dir == "" {
return nil
}
if err := os.MkdirAll(dir, 0700); err != nil {
return errors.Wrapf(err, "failed to create directory %s")
}
logrus.Debug("Mounting ", dir, target, " none bind")
return unix.Mount(dir, target, "none", unix.MS_BIND, "")
}

133
pkg/rootless/rootless.go Normal file
View File

@ -0,0 +1,133 @@
package rootless
import (
"io/ioutil"
"net"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/rootless-containers/rootlesskit/pkg/child"
"github.com/rootless-containers/rootlesskit/pkg/copyup/tmpfssymlink"
"github.com/rootless-containers/rootlesskit/pkg/network/slirp4netns"
"github.com/rootless-containers/rootlesskit/pkg/parent"
"github.com/rootless-containers/rootlesskit/pkg/port/socat"
"github.com/sirupsen/logrus"
)
var (
pipeFD = "_K3S_ROOTLESS_FD"
childEnv = "_K3S_ROOTLESS_SOCK"
Sock = ""
)
func Rootless(stateDir string) error {
defer func() {
os.Unsetenv(pipeFD)
os.Unsetenv(childEnv)
}()
hasFD := os.Getenv(pipeFD) != ""
hasChildEnv := os.Getenv(childEnv) != ""
if hasFD {
logrus.Debug("Running rootless child")
childOpt, err := createChildOpt()
if err != nil {
logrus.Fatal(err)
}
if err := child.Child(*childOpt); err != nil {
logrus.Fatal("child died", err)
}
}
if hasChildEnv {
Sock = os.Getenv(childEnv)
logrus.Debug("Running rootless process")
return setupMounts(stateDir)
}
logrus.Debug("Running rootless parent")
parentOpt, err := createParentOpt(filepath.Join(stateDir, "rootless"))
if err != nil {
logrus.Fatal(err)
}
os.Setenv(childEnv, filepath.Join(parentOpt.StateDir, parent.StateFileAPISock))
if err := parent.Parent(*parentOpt); err != nil {
logrus.Fatal(err)
}
os.Exit(0)
return nil
}
func parseCIDR(s string) (*net.IPNet, error) {
if s == "" {
return nil, nil
}
ip, ipnet, err := net.ParseCIDR(s)
if err != nil {
return nil, err
}
if !ip.Equal(ipnet.IP) {
return nil, errors.Errorf("cidr must be like 10.0.2.0/24, not like 10.0.2.100/24")
}
return ipnet, nil
}
func createParentOpt(stateDir string) (*parent.Opt, error) {
if err := os.MkdirAll(stateDir, 0755); err != nil {
return nil, errors.Wrapf(err, "failed to mkdir %s", stateDir)
}
stateDir, err := ioutil.TempDir("", "rootless")
if err != nil {
return nil, err
}
opt := &parent.Opt{
StateDir: stateDir,
}
mtu := 0
ipnet, err := parseCIDR("10.41.0.0/16")
if err != nil {
return nil, err
}
disableHostLoopback := true
binary := "slirp4netns"
if _, err := exec.LookPath(binary); err != nil {
return nil, err
}
opt.NetworkDriver = slirp4netns.NewParentDriver(binary, mtu, ipnet, disableHostLoopback, "")
opt.PortDriver, err = socat.NewParentDriver(&logrusDebugWriter{})
if err != nil {
return nil, err
}
opt.PipeFDEnvKey = pipeFD
return opt, nil
}
type logrusDebugWriter struct {
}
func (w *logrusDebugWriter) Write(p []byte) (int, error) {
s := strings.TrimSuffix(string(p), "\n")
logrus.Debug(s)
return len(p), nil
}
func createChildOpt() (*child.Opt, error) {
opt := &child.Opt{}
opt.TargetCmd = os.Args
opt.PipeFDEnvKey = pipeFD
opt.NetworkDriver = slirp4netns.NewChildDriver()
opt.CopyUpDirs = []string{"/etc", "/run"}
opt.CopyUpDriver = tmpfssymlink.NewChildDriver()
return opt, nil
}

View File

@ -0,0 +1,150 @@
package rootlessports
import (
"context"
"time"
"github.com/rancher/k3s/pkg/rootless"
coreClients "github.com/rancher/k3s/types/apis/core/v1"
"github.com/rootless-containers/rootlesskit/pkg/api/client"
"github.com/rootless-containers/rootlesskit/pkg/port"
"github.com/sirupsen/logrus"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
var (
all = "_all_"
)
func Register(ctx context.Context, httpsPort int) error {
var (
err error
rootlessClient client.Client
)
if rootless.Sock == "" {
return nil
}
coreClients := coreClients.ClientsFrom(ctx)
for i := 0; i < 30; i++ {
rootlessClient, err = client.New(rootless.Sock)
if err == nil {
break
} else {
logrus.Infof("waiting for rootless API socket %s: %v", rootless.Sock, err)
time.Sleep(1 * time.Second)
}
}
if err != nil {
return err
}
h := &handler{
rootlessClient: rootlessClient,
serviceClient: coreClients.Service,
serviceCache: coreClients.Service.Cache(),
httpsPort: httpsPort,
ctx: ctx,
}
coreClients.Service.Interface().Controller().AddHandler(ctx, "rootlessports", h.serviceChanged)
coreClients.Service.Enqueue("", all)
return nil
}
type handler struct {
rootlessClient client.Client
serviceClient coreClients.ServiceClient
serviceCache coreClients.ServiceClientCache
httpsPort int
ctx context.Context
}
func (h *handler) serviceChanged(key string, svc *v1.Service) (runtime.Object, error) {
if key != all {
h.serviceClient.Enqueue("", all)
return svc, nil
}
ports, err := h.rootlessClient.PortManager().ListPorts(h.ctx)
if err != nil {
return svc, err
}
boundPorts := map[int]int{}
for _, port := range ports {
boundPorts[port.Spec.ParentPort] = port.ID
}
toBindPort, err := h.toBindPorts()
if err != nil {
return svc, err
}
for bindPort, childBindPort := range toBindPort {
if _, ok := boundPorts[bindPort]; ok {
logrus.Debugf("Parent port %d to child already bound", bindPort)
delete(boundPorts, bindPort)
continue
}
status, err := h.rootlessClient.PortManager().AddPort(h.ctx, port.Spec{
Proto: "tcp",
ParentPort: bindPort,
ChildPort: childBindPort,
})
if err != nil {
return svc, err
}
logrus.Infof("Bound parent port %s:%d to child namespace port %d", status.Spec.ParentIP,
status.Spec.ParentPort, status.Spec.ChildPort)
}
for bindPort, id := range boundPorts {
if err := h.rootlessClient.PortManager().RemovePort(h.ctx, id); err != nil {
return svc, err
}
logrus.Infof("Removed parent port %d to child namespace", bindPort)
}
return svc, nil
}
func (h *handler) toBindPorts() (map[int]int, error) {
svcs, err := h.serviceCache.List("", labels.Everything())
if err != nil {
return nil, err
}
toBindPorts := map[int]int{
h.httpsPort: h.httpsPort,
}
for _, svc := range svcs {
for _, ingress := range svc.Status.LoadBalancer.Ingress {
if ingress.IP == "" {
continue
}
for _, port := range svc.Spec.Ports {
if port.Protocol != v1.ProtocolTCP {
continue
}
if port.Port != 0 {
if port.Port <= 1024 {
toBindPorts[10000+int(port.Port)] = int(port.Port)
} else {
toBindPorts[int(port.Port)] = int(port.Port)
}
}
}
}
}
return toBindPorts, nil
}

View File

@ -18,6 +18,7 @@ import (
"github.com/rancher/k3s/pkg/datadir" "github.com/rancher/k3s/pkg/datadir"
"github.com/rancher/k3s/pkg/deploy" "github.com/rancher/k3s/pkg/deploy"
"github.com/rancher/k3s/pkg/helm" "github.com/rancher/k3s/pkg/helm"
"github.com/rancher/k3s/pkg/rootlessports"
"github.com/rancher/k3s/pkg/servicelb" "github.com/rancher/k3s/pkg/servicelb"
"github.com/rancher/k3s/pkg/static" "github.com/rancher/k3s/pkg/static"
"github.com/rancher/k3s/pkg/tls" "github.com/rancher/k3s/pkg/tls"
@ -36,16 +37,8 @@ import (
) )
func resolveDataDir(dataDir string) (string, error) { func resolveDataDir(dataDir string) (string, error) {
if dataDir == "" { dataDir, err := datadir.Resolve(dataDir)
if os.Getuid() == 0 { return filepath.Join(dataDir, "server"), err
dataDir = "/var/lib/rancher/k3s"
} else {
dataDir = "${HOME}/.rancher/k3s"
}
}
dataDir = filepath.Join(dataDir, "server")
return resolvehome.Resolve(dataDir)
} }
func StartServer(ctx context.Context, config *Config) (string, error) { func StartServer(ctx context.Context, config *Config) (string, error) {
@ -72,7 +65,7 @@ func StartServer(ctx context.Context, config *Config) (string, error) {
} }
printTokens(certs, ip.String(), &config.TLSConfig, &config.ControlConfig) printTokens(certs, ip.String(), &config.TLSConfig, &config.ControlConfig)
writeKubeConfig(certs, &config.TLSConfig, &config.ControlConfig) writeKubeConfig(certs, &config.TLSConfig, config)
return certs, nil return certs, nil
} }
@ -121,7 +114,8 @@ func startNorman(ctx context.Context, config *Config) (string, error) {
MasterControllers: []norman.ControllerRegister{ MasterControllers: []norman.ControllerRegister{
helm.Register, helm.Register,
func(ctx context.Context) error { func(ctx context.Context) error {
return servicelb.Register(ctx, norman.GetServer(ctx).K8sClient, !config.DisableServiceLB) return servicelb.Register(ctx, norman.GetServer(ctx).K8sClient, !config.DisableServiceLB,
config.Rootless)
}, },
func(ctx context.Context) error { func(ctx context.Context) error {
dataDir := filepath.Join(controlConfig.DataDir, "static") dataDir := filepath.Join(controlConfig.DataDir, "static")
@ -138,6 +132,12 @@ func startNorman(ctx context.Context, config *Config) (string, error) {
} }
return nil return nil
}, },
func(ctx context.Context) error {
if !config.DisableServiceLB && config.Rootless {
return rootlessports.Register(ctx, config.TLSConfig.HTTPSPort)
}
return nil
},
}, },
} }
@ -156,9 +156,9 @@ func startNorman(ctx context.Context, config *Config) (string, error) {
} }
} }
func HomeKubeConfig(write bool) (string, error) { func HomeKubeConfig(write, rootless bool) (string, error) {
if write { if write {
if os.Getuid() == 0 { if os.Getuid() == 0 && !rootless {
return datadir.GlobalConfig, nil return datadir.GlobalConfig, nil
} }
return resolvehome.Resolve(datadir.HomeConfig) return resolvehome.Resolve(datadir.HomeConfig)
@ -194,30 +194,30 @@ func printTokens(certs, advertiseIP string, tlsConfig *dynamiclistener.UserConfi
} }
func writeKubeConfig(certs string, tlsConfig *dynamiclistener.UserConfig, config *config.Control) { func writeKubeConfig(certs string, tlsConfig *dynamiclistener.UserConfig, config *Config) {
clientToken := FormatToken(config.Runtime.ClientToken, certs) clientToken := FormatToken(config.ControlConfig.Runtime.ClientToken, certs)
ip := tlsConfig.BindAddress ip := tlsConfig.BindAddress
if ip == "" { if ip == "" {
ip = "localhost" ip = "localhost"
} }
url := fmt.Sprintf("https://%s:%d", ip, tlsConfig.HTTPSPort) url := fmt.Sprintf("https://%s:%d", ip, tlsConfig.HTTPSPort)
kubeConfig, err := HomeKubeConfig(true) kubeConfig, err := HomeKubeConfig(true, config.Rootless)
def := true def := true
if err != nil { if err != nil {
kubeConfig = filepath.Join(config.DataDir, "kubeconfig-k3s.yaml") kubeConfig = filepath.Join(config.ControlConfig.DataDir, "kubeconfig-k3s.yaml")
def = false def = false
} }
if config.KubeConfigOutput != "" { if config.ControlConfig.KubeConfigOutput != "" {
kubeConfig = config.KubeConfigOutput kubeConfig = config.ControlConfig.KubeConfigOutput
} }
if err = clientaccess.AgentAccessInfoToKubeConfig(kubeConfig, url, clientToken); err != nil { if err = clientaccess.AgentAccessInfoToKubeConfig(kubeConfig, url, clientToken); err != nil {
logrus.Errorf("Failed to generate kubeconfig: %v", err) logrus.Errorf("Failed to generate kubeconfig: %v", err)
} }
if config.KubeConfigMode != "" { if config.ControlConfig.KubeConfigMode != "" {
mode, err := strconv.ParseInt(config.KubeConfigMode, 8, 0) mode, err := strconv.ParseInt(config.ControlConfig.KubeConfigMode, 8, 0)
if err == nil { if err == nil {
os.Chmod(kubeConfig, os.FileMode(mode)) os.Chmod(kubeConfig, os.FileMode(mode))
} else { } else {

View File

@ -10,4 +10,5 @@ type Config struct {
DisableServiceLB bool DisableServiceLB bool
TLSConfig dynamiclistener.UserConfig TLSConfig dynamiclistener.UserConfig
ControlConfig config.Control ControlConfig config.Control
Rootless bool
} }

View File

@ -34,11 +34,12 @@ var (
trueVal = true trueVal = true
) )
func Register(ctx context.Context, kubernetes kubernetes.Interface, enabled bool) error { func Register(ctx context.Context, kubernetes kubernetes.Interface, enabled, rootless bool) error {
clients := coreclient.ClientsFrom(ctx) clients := coreclient.ClientsFrom(ctx)
appClients := appclient.ClientsFrom(ctx) appClients := appclient.ClientsFrom(ctx)
h := &handler{ h := &handler{
rootless: rootless,
enabled: enabled, enabled: enabled,
nodeCache: clients.Node.Cache(), nodeCache: clients.Node.Cache(),
podCache: clients.Pod.Cache(), podCache: clients.Pod.Cache(),
@ -59,6 +60,7 @@ func Register(ctx context.Context, kubernetes kubernetes.Interface, enabled bool
} }
type handler struct { type handler struct {
rootless bool
enabled bool enabled bool
nodeCache coreclient.NodeClientCache nodeCache coreclient.NodeClientCache
podCache coreclient.PodClientCache podCache coreclient.PodClientCache
@ -189,6 +191,11 @@ func (h *handler) podIPs(pods []*core.Pod) ([]string, error) {
for k := range ips { for k := range ips {
ipList = append(ipList, k) ipList = append(ipList, k)
} }
if len(ipList) > 0 && h.rootless {
return []string{"127.0.0.1"}, nil
}
return ipList, nil return ipList, nil
} }

View File

@ -5,4 +5,9 @@ mkdir -p $(dirname $0)/../bin
cd $(dirname $0)/../bin cd $(dirname $0)/../bin
echo Running echo Running
go run -tags "apparmor" ../main.go --debug server --disable-agent "$@" ARGS="--disable-agent"
if echo -- "$@" | grep -q rootless; then
ARGS=""
PATH=$(pwd):$PATH
fi
go run -tags "apparmor" ../main.go server $ARGS "$@"

View File

@ -4,7 +4,7 @@ source $(dirname $0)/version.sh
cd $(dirname $0)/.. cd $(dirname $0)/..
ROOT_VERSION=v0.0.1 ROOT_VERSION=v0.1.1
TRAEFIK_VERSION=1.64.0 TRAEFIK_VERSION=1.64.0
CHARTS_DIR=build/static/charts CHARTS_DIR=build/static/charts

View File

@ -22,7 +22,8 @@ import:
- package: github.com/containerd/continuity - package: github.com/containerd/continuity
version: bd77b46c8352f74eb12c85bdc01f4b90f69d66b4 version: bd77b46c8352f74eb12c85bdc01f4b90f69d66b4
- package: github.com/containerd/cri - package: github.com/containerd/cri
version: eb926cd79d3bac188dcc4ed7694fc9298f8831be version: v1.2-k3s.2
repo: https://github.com/rancher/cri.git
- package: github.com/containerd/fifo - package: github.com/containerd/fifo
version: 3d5202aec260678c48179c56f40e6f38a095738c version: 3d5202aec260678c48179c56f40e6f38a095738c
- package: github.com/containerd/go-cni - package: github.com/containerd/go-cni
@ -49,6 +50,8 @@ import:
version: v1.1.0 version: v1.1.0
- package: github.com/docker/distribution - package: github.com/docker/distribution
version: 0d3efadf0154c2b8a4e7b6621fff9809655cc580 version: 0d3efadf0154c2b8a4e7b6621fff9809655cc580
- package: github.com/docker/docker
version: c12f09bf99b54f274a5ae241dd154fa74020cbab
- package: github.com/docker/go-events - package: github.com/docker/go-events
version: 9461782956ad83b30282bf90e31fa6a70c255ba9 version: 9461782956ad83b30282bf90e31fa6a70c255ba9
- package: github.com/docker/go-metrics - package: github.com/docker/go-metrics
@ -94,6 +97,8 @@ import:
version: 1.0.3 version: 1.0.3
- package: github.com/modern-go/reflect2 - package: github.com/modern-go/reflect2
version: 1.0.1 version: 1.0.1
- package: github.com/morikuni/aec
version: 39771216ff4c63d11f5e604076f9c45e8be1067b
- package: github.com/natefinch/lumberjack - package: github.com/natefinch/lumberjack
version: aee4629129445bbdfb69aa565537dcfa16544311 version: aee4629129445bbdfb69aa565537dcfa16544311
- package: github.com/opencontainers/go-digest - package: github.com/opencontainers/go-digest
@ -113,6 +118,8 @@ import:
- package: github.com/rancher/norman - package: github.com/rancher/norman
version: 50017efee23caa79542ef685b65a7b783e0a73ca version: 50017efee23caa79542ef685b65a7b783e0a73ca
repo: https://github.com/ibuildthecloud/norman.git repo: https://github.com/ibuildthecloud/norman.git
- package: github.com/rootless-containers/rootlesskit
version: 893c1c3de71f54c301fdb85a7c0dd15c1933c159
- package: github.com/seccomp/libseccomp-golang - package: github.com/seccomp/libseccomp-golang
version: 32f571b70023028bd57d9288c20efbcb237f3ce0 version: 32f571b70023028bd57d9288c20efbcb237f3ce0
- package: github.com/sirupsen/logrus - package: github.com/sirupsen/logrus
@ -121,6 +128,8 @@ import:
version: db04d3cc01c8b54962a58ec7e491717d06cfcc16 version: db04d3cc01c8b54962a58ec7e491717d06cfcc16
- package: github.com/tchap/go-patricia - package: github.com/tchap/go-patricia
version: v2.2.6 version: v2.2.6
- package: github.com/theckman/go-flock
version: v0.7.1
- package: github.com/urfave/cli - package: github.com/urfave/cli
version: 8e01ec4cd3e2d84ab2fe90d8210528ffbb06d8ff version: 8e01ec4cd3e2d84ab2fe90d8210528ffbb06d8ff
- package: github.com/xeipuuv/gojsonpointer - package: github.com/xeipuuv/gojsonpointer
@ -155,148 +164,144 @@ import:
- package: gopkg.in/yaml.v2 - package: gopkg.in/yaml.v2
version: v2.2.1 version: v2.2.1
- package: k8s.io/kubernetes - package: k8s.io/kubernetes
version: v1.14.1-k3s.1 version: v1.14.1-k3s.2
repo: https://github.com/rancher/k3s.git repo: https://github.com/rancher/k3s.git
transitive: true transitive: true
staging: true staging: true
- package: vbom.ml/util - package: github.com/prometheus/client_model
version: db5cfe13f5cc80a4990d98e2e1b0707a4d1a5394 version: model-0.0.2-12-gfa8ad6fec33561
- package: github.com/mxk/go-flowrate
version: cca7078d478f8520f85629ad7c68962d31ed7682
- package: github.com/ugorji/go
version: bdcc60b419d136a85cdf2e7cbcac34b3f1cd6e57
- package: github.com/sigma/go-inotify
version: c87b6cf5033d2c6486046f045eeebdc3d910fd38
- package: github.com/spf13/cobra
version: v0.0.1-34-gc439c4fa093711
- package: github.com/liggitt/tabwriter
version: 89fcab3d43de07060e4fd4c1547430ed57e87f24
- package: github.com/mindprince/gonvml
version: fee913ce8fb235edf54739d259ca0ecc226c7b8a
- package: github.com/mistifyio/go-zfs
version: v2.1.1-5-g1b4ae6fb4e77b0
- package: github.com/peterbourgon/diskv
version: v2.0.1
- package: github.com/russross/blackfriday
version: v1.4-2-g300106c228d52c
- package: github.com/inconshreveable/mousetrap
version: v1.0
- package: github.com/imdario/mergo - package: github.com/imdario/mergo
version: v0.3.5 version: v0.3.5
- package: github.com/vishvananda/netns - package: github.com/fsnotify/fsnotify
version: be1fbeda19366dea804f00efff2dd73a1642fdcc version: v1.3.1-1-gf12c6236fe7b5c
- package: github.com/prometheus/client_golang - package: github.com/cloudflare/cfssl
version: v0.9.2 version: 1.3.2-21-g56268a613adfed
- package: github.com/mattn/go-shellwords - package: github.com/spf13/cobra
version: v1.0.3-20-gf8471b0a71ded0 version: v0.0.1-34-gc439c4fa093711
- package: github.com/prometheus/procfs
version: 65c1f6f8f0fc1e2185eb9863a3bc751496404259
- package: github.com/vishvananda/netlink - package: github.com/vishvananda/netlink
version: b2de5d10e38ecce8607e6b438b6d174f389a004e version: b2de5d10e38ecce8607e6b438b6d174f389a004e
- package: github.com/chai2010/gettext-go - package: github.com/google/certificate-transparency-go
version: c6fed771bfd517099caf0f7a961671fa8ed08723 version: v1.0.21
- package: github.com/container-storage-interface/spec - package: github.com/coreos/pkg
version: v1.1.0 version: v4
- package: github.com/hashicorp/golang-lru - package: vbom.ml/util
version: v0.5.0 version: db5cfe13f5cc80a4990d98e2e1b0707a4d1a5394
- package: github.com/spf13/pflag - package: github.com/miekg/dns
version: v1.0.1 version: 5d001d020961ae1c184f9f8152fdc73810481677
- package: github.com/munnerz/goautoneg
version: a547fc61f48d567d5b4ec6f8aee5573d8efce11d
- package: github.com/google/btree
version: 7d79101e329e5a3adf994758c578dab82b90c017
- package: github.com/karrick/godirwalk
version: v1.7.5
- package: github.com/coreos/etcd
version: v3.3.10
- package: github.com/gregjones/httpcache
version: 787624de3eb7bd915c329cba748687a3b22666a6
- package: github.com/armon/circbuf
version: bbbad097214e2918d8543d5201d12bfd7bca254d
- package: github.com/docker/libnetwork
version: v0.8.0-dev.2-1265-ga9cd636e378982
- package: github.com/jonboulle/clockwork
version: 72f9bd7c4e0c2a40055ab3d0f09654f730cce982
- package: k8s.io/gengo
version: 51747d6e00da1fc578d5a333a93bb2abcbce7a95
- package: github.com/JeffAshton/win_pdh
version: 76bb4ee9f0ab50f77826f2a2ee7fb9d3880d6ec2
- package: github.com/robfig/cron
version: v1-53-gdf38d32658d878
- package: github.com/shurcooL/sanitized_anchor_name - package: github.com/shurcooL/sanitized_anchor_name
version: 10ef21a441db47d8b13ebcc5fd2310f636973c77 version: 10ef21a441db47d8b13ebcc5fd2310f636973c77
- package: github.com/MakeNowJust/heredoc - package: github.com/ugorji/go
version: bb23615498cded5e105af4ce27de75b089cbe851 version: bdcc60b419d136a85cdf2e7cbcac34b3f1cd6e57
- package: github.com/fatih/camelcase
version: f6a740d52f961c60348ebb109adde9f4635d7540
- package: github.com/hashicorp/golang-lru
version: v0.5.0
- package: github.com/prometheus/common - package: github.com/prometheus/common
version: v0.2.0 version: v0.2.0
- package: github.com/coreos/go-semver - package: github.com/sigma/go-inotify
version: v0.2.0-9-ge214231b295a8e version: c87b6cf5033d2c6486046f045eeebdc3d910fd38
- package: sigs.k8s.io/yaml
version: v1.1.0
- package: k8s.io/heapster
version: v1.2.0-beta.1
- package: github.com/golang/groupcache
version: 02826c3e79038b59d737d3b1c0a1d937f71a4433
- package: gopkg.in/square/go-jose.v2
version: v2.1.6-4-g89060dee6a84df
- package: github.com/Azure/go-ansiterm
version: d6e3b3328b783f23731bc4d058875b0371ff8109
- package: github.com/mattn/go-shellwords
version: v1.0.3-20-gf8471b0a71ded0
- package: github.com/prometheus/client_golang
version: v0.9.2
- package: github.com/docker/go-units
version: v0.3.1-11-g9e638d38cf6977
- package: github.com/chai2010/gettext-go
version: c6fed771bfd517099caf0f7a961671fa8ed08723
- package: github.com/daviddengcn/go-colortext
version: 511bcaf42ccd42c38aba7427b6673277bf19e2a1
- package: github.com/vishvananda/netns
version: be1fbeda19366dea804f00efff2dd73a1642fdcc
- package: github.com/docker/go-connections
version: v0.3.0
- package: gopkg.in/natefinch/lumberjack.v2
version: v1.0-16-g20b71e5b60d756
- package: github.com/russross/blackfriday
version: v1.4-2-g300106c228d52c
- package: github.com/google/cadvisor - package: github.com/google/cadvisor
version: v0.33.1-k3s.1 version: v0.33.1-k3s.1
repo: https://github.com/ibuildthecloud/cadvisor.git repo: https://github.com/ibuildthecloud/cadvisor.git
- package: github.com/cyphar/filepath-securejoin - package: github.com/spf13/pflag
version: v0.2.1-1-gae69057f2299fb version: v1.0.1
- package: github.com/ibuildthecloud/kvsql - package: github.com/mistifyio/go-zfs
version: 0e798b1475327aadf3b8da5d2d1f99bb93dfd667 version: v2.1.1-5-g1b4ae6fb4e77b0
- package: github.com/JeffAshton/win_pdh
version: 76bb4ee9f0ab50f77826f2a2ee7fb9d3880d6ec2
- package: github.com/prometheus/client_model
version: model-0.0.2-12-gfa8ad6fec33561
- package: github.com/miekg/dns
version: 5d001d020961ae1c184f9f8152fdc73810481677
- package: github.com/daviddengcn/go-colortext
version: 511bcaf42ccd42c38aba7427b6673277bf19e2a1
- package: github.com/gregjones/httpcache
version: 787624de3eb7bd915c329cba748687a3b22666a6
- package: github.com/karrick/godirwalk
version: v1.7.5
- package: k8s.io/gengo
version: 51747d6e00da1fc578d5a333a93bb2abcbce7a95
- package: k8s.io/heapster
version: v1.2.0-beta.1
- package: github.com/checkpoint-restore/go-criu
version: v3.11
- package: github.com/google/btree
version: 7d79101e329e5a3adf994758c578dab82b90c017
- package: github.com/google/certificate-transparency-go
version: v1.0.21
- package: github.com/robfig/cron
version: v1-53-gdf38d32658d878
- package: github.com/mrunalp/fileutils
version: 4ee1cc9a80582a0c75febdd5cfa779ee4361cbca
- package: github.com/Nvveen/Gotty
version: cd527374f1e5bff4938207604a14f2e38a9cf512
- package: github.com/exponent-io/jsonpath
version: d6023ce2651d8eafb5c75bb0c7167536102ec9f5
- package: github.com/fsnotify/fsnotify
version: v1.3.1-1-gf12c6236fe7b5c
- package: github.com/jteeuwen/go-bindata
version: v3.0.7-72-ga0ff2567cfb709
- package: gopkg.in/square/go-jose.v2
version: v2.1.6-4-g89060dee6a84df
- package: k8s.io/klog
version: v0.2.0-14-g8e90cee79f8237
- package: github.com/munnerz/goautoneg
version: a547fc61f48d567d5b4ec6f8aee5573d8efce11d
- package: github.com/docker/go-units
version: v0.3.1-11-g9e638d38cf6977
- package: github.com/fatih/camelcase
version: f6a740d52f961c60348ebb109adde9f4635d7540
- package: k8s.io/utils
version: c2654d5206da6b7b6ace12841e8f359bb89b443c
- package: github.com/coreos/etcd
version: v3.3.10
- package: github.com/docker/go-connections
version: v0.3.0
- package: github.com/golang/groupcache
version: 02826c3e79038b59d737d3b1c0a1d937f71a4433
- package: gopkg.in/natefinch/lumberjack.v2
version: v1.0-16-g20b71e5b60d756
- package: github.com/mitchellh/go-wordwrap - package: github.com/mitchellh/go-wordwrap
version: ad45545899c7b13c020ea92b2072220eefad42b8 version: ad45545899c7b13c020ea92b2072220eefad42b8
- package: github.com/docker/libnetwork - package: github.com/ibuildthecloud/kvsql
version: v0.8.0-dev.2-1265-ga9cd636e378982 version: 0e798b1475327aadf3b8da5d2d1f99bb93dfd667
- package: sigs.k8s.io/yaml
version: v1.1.0
- package: github.com/euank/go-kmsg-parser
version: v2.0.0
- package: github.com/evanphx/json-patch
version: v4.1.0-19-g5858425f75500d
- package: github.com/lithammer/dedent
version: v1.1.0
- package: github.com/jonboulle/clockwork
version: 72f9bd7c4e0c2a40055ab3d0f09654f730cce982
- package: github.com/cloudflare/cfssl
version: 1.3.2-21-g56268a613adfed
- package: github.com/docker/docker
version: docs-v1.12.0-rc4-2016-07-15-9510-ga9fbbdc8dd8794
- package: github.com/coreos/pkg
version: v4
- package: github.com/Azure/go-ansiterm
version: d6e3b3328b783f23731bc4d058875b0371ff8109
- package: github.com/armon/circbuf
version: bbbad097214e2918d8543d5201d12bfd7bca254d
- package: golang.org/x/tools
version: 7f7074d5bcfd282eb16bc382b0bb3da762461985
- package: github.com/pborman/uuid
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
- package: github.com/googleapis/gnostic - package: github.com/googleapis/gnostic
version: 0c5108395e2debce0d731cf0287ddf7242066aba version: 0c5108395e2debce0d731cf0287ddf7242066aba
- package: github.com/inconshreveable/mousetrap
version: v1.0
- package: github.com/container-storage-interface/spec
version: v1.1.0
- package: github.com/cyphar/filepath-securejoin
version: v0.2.1-1-gae69057f2299fb
- package: github.com/mrunalp/fileutils
version: 4ee1cc9a80582a0c75febdd5cfa779ee4361cbca
- package: github.com/lithammer/dedent
version: v1.1.0
- package: github.com/mxk/go-flowrate
version: cca7078d478f8520f85629ad7c68962d31ed7682
- package: github.com/peterbourgon/diskv
version: v2.0.1
- package: github.com/prometheus/procfs
version: 65c1f6f8f0fc1e2185eb9863a3bc751496404259
- package: github.com/jteeuwen/go-bindata
version: v3.0.7-72-ga0ff2567cfb709
- package: github.com/pborman/uuid
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
- package: github.com/checkpoint-restore/go-criu
version: v3.11
- package: github.com/MakeNowJust/heredoc
version: bb23615498cded5e105af4ce27de75b089cbe851
- package: github.com/liggitt/tabwriter
version: 89fcab3d43de07060e4fd4c1547430ed57e87f24
- package: github.com/exponent-io/jsonpath
version: d6023ce2651d8eafb5c75bb0c7167536102ec9f5
- package: golang.org/x/tools
version: 7f7074d5bcfd282eb16bc382b0bb3da762461985
- package: github.com/evanphx/json-patch
version: v4.1.0-19-g5858425f75500d
- package: github.com/mindprince/gonvml
version: fee913ce8fb235edf54739d259ca0ecc226c7b8a
- package: k8s.io/klog
version: v0.2.0-14-g8e90cee79f8237
- package: github.com/coreos/go-semver
version: v0.2.0-9-ge214231b295a8e
- package: github.com/euank/go-kmsg-parser
version: v2.0.0
- package: k8s.io/utils
version: c2654d5206da6b7b6ace12841e8f359bb89b443c

View File

@ -9,7 +9,7 @@ package=github.com/opencontainers/runc/libcontainer/nsenter
package=github.com/opencontainers/runc/libcontainer/specconv package=github.com/opencontainers/runc/libcontainer/specconv
package=github.com/opencontainers/runc/contrib/cmd/recvtty package=github.com/opencontainers/runc/contrib/cmd/recvtty
k8s.io/kubernetes v1.14.1-k3s.1 https://github.com/rancher/k3s.git transitive=true,staging=true k8s.io/kubernetes v1.14.1-k3s.2 https://github.com/rancher/k3s.git transitive=true,staging=true
github.com/rancher/norman 50017efee23caa79542ef685b65a7b783e0a73ca https://github.com/ibuildthecloud/norman.git github.com/rancher/norman 50017efee23caa79542ef685b65a7b783e0a73ca https://github.com/ibuildthecloud/norman.git
github.com/coreos/flannel 823afe66b2266bf71f5bec24e6e28b26d70cfc7c https://github.com/ibuildthecloud/flannel.git github.com/coreos/flannel 823afe66b2266bf71f5bec24e6e28b26d70cfc7c https://github.com/ibuildthecloud/flannel.git
@ -85,14 +85,14 @@ go.etcd.io/bbolt v1.3.1-etcd.8
github.com/kubernetes-sigs/cri-tools c465773e3ad8c941d1756c0a467d550b04a8f65b https://github.com/ibuildthecloud/cri-tools.git github.com/kubernetes-sigs/cri-tools c465773e3ad8c941d1756c0a467d550b04a8f65b https://github.com/ibuildthecloud/cri-tools.git
# cri dependencies # cri dependencies
github.com/containerd/cri eb926cd79d3bac188dcc4ed7694fc9298f8831be # release/1.2 branch github.com/containerd/cri v1.2-k3s.2 https://github.com/rancher/cri.git
github.com/containerd/go-cni 40bcf8ec8acd7372be1d77031d585d5d8e561c90 github.com/containerd/go-cni 40bcf8ec8acd7372be1d77031d585d5d8e561c90
github.com/blang/semver v3.1.0 github.com/blang/semver v3.1.0
github.com/containernetworking/cni v0.6.0 github.com/containernetworking/cni v0.6.0
#github.com/containernetworking/plugins v0.7.5 #github.com/containernetworking/plugins v0.7.5
github.com/davecgh/go-spew v1.1.0 github.com/davecgh/go-spew v1.1.0
github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580 github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580
#github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00 github.com/docker/docker c12f09bf99b54f274a5ae241dd154fa74020cbab
github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528 github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
github.com/emicklei/go-restful v2.2.1 github.com/emicklei/go-restful v2.2.1
github.com/ghodss/yaml v1.0.0 github.com/ghodss/yaml v1.0.0
@ -115,3 +115,9 @@ golang.org/x/oauth2 a6bd8cefa1811bd24b86f8902872e4e8225f74c4
golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631
gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
gopkg.in/yaml.v2 v2.2.1 gopkg.in/yaml.v2 v2.2.1
# rootless
github.com/rootless-containers/rootlesskit 893c1c3de71f54c301fdb85a7c0dd15c1933c159
github.com/theckman/go-flock v0.7.1
github.com/morikuni/aec 39771216ff4c63d11f5e604076f9c45e8be1067b

View File

@ -1,26 +0,0 @@
Copyright (c) 2012, Neal van Veen (nealvanveen@gmail.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.

View File

@ -1,5 +0,0 @@
Gotty is a library written in Go that determines and reads termcap database
files to produce an interface for interacting with the capabilities of a
terminal.
See the godoc documentation or the source code for more information about
function usage.

View File

@ -1,3 +0,0 @@
gotty.go:// TODO add more concurrency to name lookup, look for more opportunities.
all:// TODO add more documentation, with function usage in a doc.go file.
all:// TODO add more testing/benchmarking with go test.

View File

@ -1,514 +0,0 @@
// Copyright 2012 Neal van Veen. All rights reserved.
// Usage of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package gotty
// Boolean capabilities
var BoolAttr = [...]string{
"auto_left_margin", "bw",
"auto_right_margin", "am",
"no_esc_ctlc", "xsb",
"ceol_standout_glitch", "xhp",
"eat_newline_glitch", "xenl",
"erase_overstrike", "eo",
"generic_type", "gn",
"hard_copy", "hc",
"has_meta_key", "km",
"has_status_line", "hs",
"insert_null_glitch", "in",
"memory_above", "da",
"memory_below", "db",
"move_insert_mode", "mir",
"move_standout_mode", "msgr",
"over_strike", "os",
"status_line_esc_ok", "eslok",
"dest_tabs_magic_smso", "xt",
"tilde_glitch", "hz",
"transparent_underline", "ul",
"xon_xoff", "nxon",
"needs_xon_xoff", "nxon",
"prtr_silent", "mc5i",
"hard_cursor", "chts",
"non_rev_rmcup", "nrrmc",
"no_pad_char", "npc",
"non_dest_scroll_region", "ndscr",
"can_change", "ccc",
"back_color_erase", "bce",
"hue_lightness_saturation", "hls",
"col_addr_glitch", "xhpa",
"cr_cancels_micro_mode", "crxm",
"has_print_wheel", "daisy",
"row_addr_glitch", "xvpa",
"semi_auto_right_margin", "sam",
"cpi_changes_res", "cpix",
"lpi_changes_res", "lpix",
"backspaces_with_bs", "",
"crt_no_scrolling", "",
"no_correctly_working_cr", "",
"gnu_has_meta_key", "",
"linefeed_is_newline", "",
"has_hardware_tabs", "",
"return_does_clr_eol", "",
}
// Numerical capabilities
var NumAttr = [...]string{
"columns", "cols",
"init_tabs", "it",
"lines", "lines",
"lines_of_memory", "lm",
"magic_cookie_glitch", "xmc",
"padding_baud_rate", "pb",
"virtual_terminal", "vt",
"width_status_line", "wsl",
"num_labels", "nlab",
"label_height", "lh",
"label_width", "lw",
"max_attributes", "ma",
"maximum_windows", "wnum",
"max_colors", "colors",
"max_pairs", "pairs",
"no_color_video", "ncv",
"buffer_capacity", "bufsz",
"dot_vert_spacing", "spinv",
"dot_horz_spacing", "spinh",
"max_micro_address", "maddr",
"max_micro_jump", "mjump",
"micro_col_size", "mcs",
"micro_line_size", "mls",
"number_of_pins", "npins",
"output_res_char", "orc",
"output_res_line", "orl",
"output_res_horz_inch", "orhi",
"output_res_vert_inch", "orvi",
"print_rate", "cps",
"wide_char_size", "widcs",
"buttons", "btns",
"bit_image_entwining", "bitwin",
"bit_image_type", "bitype",
"magic_cookie_glitch_ul", "",
"carriage_return_delay", "",
"new_line_delay", "",
"backspace_delay", "",
"horizontal_tab_delay", "",
"number_of_function_keys", "",
}
// String capabilities
var StrAttr = [...]string{
"back_tab", "cbt",
"bell", "bel",
"carriage_return", "cr",
"change_scroll_region", "csr",
"clear_all_tabs", "tbc",
"clear_screen", "clear",
"clr_eol", "el",
"clr_eos", "ed",
"column_address", "hpa",
"command_character", "cmdch",
"cursor_address", "cup",
"cursor_down", "cud1",
"cursor_home", "home",
"cursor_invisible", "civis",
"cursor_left", "cub1",
"cursor_mem_address", "mrcup",
"cursor_normal", "cnorm",
"cursor_right", "cuf1",
"cursor_to_ll", "ll",
"cursor_up", "cuu1",
"cursor_visible", "cvvis",
"delete_character", "dch1",
"delete_line", "dl1",
"dis_status_line", "dsl",
"down_half_line", "hd",
"enter_alt_charset_mode", "smacs",
"enter_blink_mode", "blink",
"enter_bold_mode", "bold",
"enter_ca_mode", "smcup",
"enter_delete_mode", "smdc",
"enter_dim_mode", "dim",
"enter_insert_mode", "smir",
"enter_secure_mode", "invis",
"enter_protected_mode", "prot",
"enter_reverse_mode", "rev",
"enter_standout_mode", "smso",
"enter_underline_mode", "smul",
"erase_chars", "ech",
"exit_alt_charset_mode", "rmacs",
"exit_attribute_mode", "sgr0",
"exit_ca_mode", "rmcup",
"exit_delete_mode", "rmdc",
"exit_insert_mode", "rmir",
"exit_standout_mode", "rmso",
"exit_underline_mode", "rmul",
"flash_screen", "flash",
"form_feed", "ff",
"from_status_line", "fsl",
"init_1string", "is1",
"init_2string", "is2",
"init_3string", "is3",
"init_file", "if",
"insert_character", "ich1",
"insert_line", "il1",
"insert_padding", "ip",
"key_backspace", "kbs",
"key_catab", "ktbc",
"key_clear", "kclr",
"key_ctab", "kctab",
"key_dc", "kdch1",
"key_dl", "kdl1",
"key_down", "kcud1",
"key_eic", "krmir",
"key_eol", "kel",
"key_eos", "ked",
"key_f0", "kf0",
"key_f1", "kf1",
"key_f10", "kf10",
"key_f2", "kf2",
"key_f3", "kf3",
"key_f4", "kf4",
"key_f5", "kf5",
"key_f6", "kf6",
"key_f7", "kf7",
"key_f8", "kf8",
"key_f9", "kf9",
"key_home", "khome",
"key_ic", "kich1",
"key_il", "kil1",
"key_left", "kcub1",
"key_ll", "kll",
"key_npage", "knp",
"key_ppage", "kpp",
"key_right", "kcuf1",
"key_sf", "kind",
"key_sr", "kri",
"key_stab", "khts",
"key_up", "kcuu1",
"keypad_local", "rmkx",
"keypad_xmit", "smkx",
"lab_f0", "lf0",
"lab_f1", "lf1",
"lab_f10", "lf10",
"lab_f2", "lf2",
"lab_f3", "lf3",
"lab_f4", "lf4",
"lab_f5", "lf5",
"lab_f6", "lf6",
"lab_f7", "lf7",
"lab_f8", "lf8",
"lab_f9", "lf9",
"meta_off", "rmm",
"meta_on", "smm",
"newline", "_glitch",
"pad_char", "npc",
"parm_dch", "dch",
"parm_delete_line", "dl",
"parm_down_cursor", "cud",
"parm_ich", "ich",
"parm_index", "indn",
"parm_insert_line", "il",
"parm_left_cursor", "cub",
"parm_right_cursor", "cuf",
"parm_rindex", "rin",
"parm_up_cursor", "cuu",
"pkey_key", "pfkey",
"pkey_local", "pfloc",
"pkey_xmit", "pfx",
"print_screen", "mc0",
"prtr_off", "mc4",
"prtr_on", "mc5",
"repeat_char", "rep",
"reset_1string", "rs1",
"reset_2string", "rs2",
"reset_3string", "rs3",
"reset_file", "rf",
"restore_cursor", "rc",
"row_address", "mvpa",
"save_cursor", "row_address",
"scroll_forward", "ind",
"scroll_reverse", "ri",
"set_attributes", "sgr",
"set_tab", "hts",
"set_window", "wind",
"tab", "s_magic_smso",
"to_status_line", "tsl",
"underline_char", "uc",
"up_half_line", "hu",
"init_prog", "iprog",
"key_a1", "ka1",
"key_a3", "ka3",
"key_b2", "kb2",
"key_c1", "kc1",
"key_c3", "kc3",
"prtr_non", "mc5p",
"char_padding", "rmp",
"acs_chars", "acsc",
"plab_norm", "pln",
"key_btab", "kcbt",
"enter_xon_mode", "smxon",
"exit_xon_mode", "rmxon",
"enter_am_mode", "smam",
"exit_am_mode", "rmam",
"xon_character", "xonc",
"xoff_character", "xoffc",
"ena_acs", "enacs",
"label_on", "smln",
"label_off", "rmln",
"key_beg", "kbeg",
"key_cancel", "kcan",
"key_close", "kclo",
"key_command", "kcmd",
"key_copy", "kcpy",
"key_create", "kcrt",
"key_end", "kend",
"key_enter", "kent",
"key_exit", "kext",
"key_find", "kfnd",
"key_help", "khlp",
"key_mark", "kmrk",
"key_message", "kmsg",
"key_move", "kmov",
"key_next", "knxt",
"key_open", "kopn",
"key_options", "kopt",
"key_previous", "kprv",
"key_print", "kprt",
"key_redo", "krdo",
"key_reference", "kref",
"key_refresh", "krfr",
"key_replace", "krpl",
"key_restart", "krst",
"key_resume", "kres",
"key_save", "ksav",
"key_suspend", "kspd",
"key_undo", "kund",
"key_sbeg", "kBEG",
"key_scancel", "kCAN",
"key_scommand", "kCMD",
"key_scopy", "kCPY",
"key_screate", "kCRT",
"key_sdc", "kDC",
"key_sdl", "kDL",
"key_select", "kslt",
"key_send", "kEND",
"key_seol", "kEOL",
"key_sexit", "kEXT",
"key_sfind", "kFND",
"key_shelp", "kHLP",
"key_shome", "kHOM",
"key_sic", "kIC",
"key_sleft", "kLFT",
"key_smessage", "kMSG",
"key_smove", "kMOV",
"key_snext", "kNXT",
"key_soptions", "kOPT",
"key_sprevious", "kPRV",
"key_sprint", "kPRT",
"key_sredo", "kRDO",
"key_sreplace", "kRPL",
"key_sright", "kRIT",
"key_srsume", "kRES",
"key_ssave", "kSAV",
"key_ssuspend", "kSPD",
"key_sundo", "kUND",
"req_for_input", "rfi",
"key_f11", "kf11",
"key_f12", "kf12",
"key_f13", "kf13",
"key_f14", "kf14",
"key_f15", "kf15",
"key_f16", "kf16",
"key_f17", "kf17",
"key_f18", "kf18",
"key_f19", "kf19",
"key_f20", "kf20",
"key_f21", "kf21",
"key_f22", "kf22",
"key_f23", "kf23",
"key_f24", "kf24",
"key_f25", "kf25",
"key_f26", "kf26",
"key_f27", "kf27",
"key_f28", "kf28",
"key_f29", "kf29",
"key_f30", "kf30",
"key_f31", "kf31",
"key_f32", "kf32",
"key_f33", "kf33",
"key_f34", "kf34",
"key_f35", "kf35",
"key_f36", "kf36",
"key_f37", "kf37",
"key_f38", "kf38",
"key_f39", "kf39",
"key_f40", "kf40",
"key_f41", "kf41",
"key_f42", "kf42",
"key_f43", "kf43",
"key_f44", "kf44",
"key_f45", "kf45",
"key_f46", "kf46",
"key_f47", "kf47",
"key_f48", "kf48",
"key_f49", "kf49",
"key_f50", "kf50",
"key_f51", "kf51",
"key_f52", "kf52",
"key_f53", "kf53",
"key_f54", "kf54",
"key_f55", "kf55",
"key_f56", "kf56",
"key_f57", "kf57",
"key_f58", "kf58",
"key_f59", "kf59",
"key_f60", "kf60",
"key_f61", "kf61",
"key_f62", "kf62",
"key_f63", "kf63",
"clr_bol", "el1",
"clear_margins", "mgc",
"set_left_margin", "smgl",
"set_right_margin", "smgr",
"label_format", "fln",
"set_clock", "sclk",
"display_clock", "dclk",
"remove_clock", "rmclk",
"create_window", "cwin",
"goto_window", "wingo",
"hangup", "hup",
"dial_phone", "dial",
"quick_dial", "qdial",
"tone", "tone",
"pulse", "pulse",
"flash_hook", "hook",
"fixed_pause", "pause",
"wait_tone", "wait",
"user0", "u0",
"user1", "u1",
"user2", "u2",
"user3", "u3",
"user4", "u4",
"user5", "u5",
"user6", "u6",
"user7", "u7",
"user8", "u8",
"user9", "u9",
"orig_pair", "op",
"orig_colors", "oc",
"initialize_color", "initc",
"initialize_pair", "initp",
"set_color_pair", "scp",
"set_foreground", "setf",
"set_background", "setb",
"change_char_pitch", "cpi",
"change_line_pitch", "lpi",
"change_res_horz", "chr",
"change_res_vert", "cvr",
"define_char", "defc",
"enter_doublewide_mode", "swidm",
"enter_draft_quality", "sdrfq",
"enter_italics_mode", "sitm",
"enter_leftward_mode", "slm",
"enter_micro_mode", "smicm",
"enter_near_letter_quality", "snlq",
"enter_normal_quality", "snrmq",
"enter_shadow_mode", "sshm",
"enter_subscript_mode", "ssubm",
"enter_superscript_mode", "ssupm",
"enter_upward_mode", "sum",
"exit_doublewide_mode", "rwidm",
"exit_italics_mode", "ritm",
"exit_leftward_mode", "rlm",
"exit_micro_mode", "rmicm",
"exit_shadow_mode", "rshm",
"exit_subscript_mode", "rsubm",
"exit_superscript_mode", "rsupm",
"exit_upward_mode", "rum",
"micro_column_address", "mhpa",
"micro_down", "mcud1",
"micro_left", "mcub1",
"micro_right", "mcuf1",
"micro_row_address", "mvpa",
"micro_up", "mcuu1",
"order_of_pins", "porder",
"parm_down_micro", "mcud",
"parm_left_micro", "mcub",
"parm_right_micro", "mcuf",
"parm_up_micro", "mcuu",
"select_char_set", "scs",
"set_bottom_margin", "smgb",
"set_bottom_margin_parm", "smgbp",
"set_left_margin_parm", "smglp",
"set_right_margin_parm", "smgrp",
"set_top_margin", "smgt",
"set_top_margin_parm", "smgtp",
"start_bit_image", "sbim",
"start_char_set_def", "scsd",
"stop_bit_image", "rbim",
"stop_char_set_def", "rcsd",
"subscript_characters", "subcs",
"superscript_characters", "supcs",
"these_cause_cr", "docr",
"zero_motion", "zerom",
"char_set_names", "csnm",
"key_mouse", "kmous",
"mouse_info", "minfo",
"req_mouse_pos", "reqmp",
"get_mouse", "getm",
"set_a_foreground", "setaf",
"set_a_background", "setab",
"pkey_plab", "pfxl",
"device_type", "devt",
"code_set_init", "csin",
"set0_des_seq", "s0ds",
"set1_des_seq", "s1ds",
"set2_des_seq", "s2ds",
"set3_des_seq", "s3ds",
"set_lr_margin", "smglr",
"set_tb_margin", "smgtb",
"bit_image_repeat", "birep",
"bit_image_newline", "binel",
"bit_image_carriage_return", "bicr",
"color_names", "colornm",
"define_bit_image_region", "defbi",
"end_bit_image_region", "endbi",
"set_color_band", "setcolor",
"set_page_length", "slines",
"display_pc_char", "dispc",
"enter_pc_charset_mode", "smpch",
"exit_pc_charset_mode", "rmpch",
"enter_scancode_mode", "smsc",
"exit_scancode_mode", "rmsc",
"pc_term_options", "pctrm",
"scancode_escape", "scesc",
"alt_scancode_esc", "scesa",
"enter_horizontal_hl_mode", "ehhlm",
"enter_left_hl_mode", "elhlm",
"enter_low_hl_mode", "elohlm",
"enter_right_hl_mode", "erhlm",
"enter_top_hl_mode", "ethlm",
"enter_vertical_hl_mode", "evhlm",
"set_a_attributes", "sgr1",
"set_pglen_inch", "slength",
"termcap_init2", "",
"termcap_reset", "",
"linefeed_if_not_lf", "",
"backspace_if_not_bs", "",
"other_non_function_keys", "",
"arrow_key_map", "",
"acs_ulcorner", "",
"acs_llcorner", "",
"acs_urcorner", "",
"acs_lrcorner", "",
"acs_ltee", "",
"acs_rtee", "",
"acs_btee", "",
"acs_ttee", "",
"acs_hline", "",
"acs_vline", "",
"acs_plus", "",
"memory_lock", "",
"memory_unlock", "",
"box_chars_1", "",
}

View File

@ -1,238 +0,0 @@
// Copyright 2012 Neal van Veen. All rights reserved.
// Usage of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Gotty is a Go-package for reading and parsing the terminfo database
package gotty
// TODO add more concurrency to name lookup, look for more opportunities.
import (
"encoding/binary"
"errors"
"fmt"
"os"
"reflect"
"strings"
"sync"
)
// Open a terminfo file by the name given and construct a TermInfo object.
// If something went wrong reading the terminfo database file, an error is
// returned.
func OpenTermInfo(termName string) (*TermInfo, error) {
var term *TermInfo
var err error
// Find the environment variables
termloc := os.Getenv("TERMINFO")
if len(termloc) == 0 {
// Search like ncurses
locations := []string{os.Getenv("HOME") + "/.terminfo/", "/etc/terminfo/",
"/lib/terminfo/", "/usr/share/terminfo/"}
var path string
for _, str := range locations {
// Construct path
path = str + string(termName[0]) + "/" + termName
// Check if path can be opened
file, _ := os.Open(path)
if file != nil {
// Path can open, fall out and use current path
file.Close()
break
}
}
if len(path) > 0 {
term, err = readTermInfo(path)
} else {
err = errors.New(fmt.Sprintf("No terminfo file(-location) found"))
}
}
return term, err
}
// Open a terminfo file from the environment variable containing the current
// terminal name and construct a TermInfo object. If something went wrong
// reading the terminfo database file, an error is returned.
func OpenTermInfoEnv() (*TermInfo, error) {
termenv := os.Getenv("TERM")
return OpenTermInfo(termenv)
}
// Return an attribute by the name attr provided. If none can be found,
// an error is returned.
func (term *TermInfo) GetAttribute(attr string) (stacker, error) {
// Channel to store the main value in.
var value stacker
// Add a blocking WaitGroup
var block sync.WaitGroup
// Keep track of variable being written.
written := false
// Function to put into goroutine.
f := func(ats interface{}) {
var ok bool
var v stacker
// Switch on type of map to use and assign value to it.
switch reflect.TypeOf(ats).Elem().Kind() {
case reflect.Bool:
v, ok = ats.(map[string]bool)[attr]
case reflect.Int16:
v, ok = ats.(map[string]int16)[attr]
case reflect.String:
v, ok = ats.(map[string]string)[attr]
}
// If ok, a value is found, so we can write.
if ok {
value = v
written = true
}
// Goroutine is done
block.Done()
}
block.Add(3)
// Go for all 3 attribute lists.
go f(term.boolAttributes)
go f(term.numAttributes)
go f(term.strAttributes)
// Wait until every goroutine is done.
block.Wait()
// If a value has been written, return it.
if written {
return value, nil
}
// Otherwise, error.
return nil, fmt.Errorf("Erorr finding attribute")
}
// Return an attribute by the name attr provided. If none can be found,
// an error is returned. A name is first converted to its termcap value.
func (term *TermInfo) GetAttributeName(name string) (stacker, error) {
tc := GetTermcapName(name)
return term.GetAttribute(tc)
}
// A utility function that finds and returns the termcap equivalent of a
// variable name.
func GetTermcapName(name string) string {
// Termcap name
var tc string
// Blocking group
var wait sync.WaitGroup
// Function to put into a goroutine
f := func(attrs []string) {
// Find the string corresponding to the name
for i, s := range attrs {
if s == name {
tc = attrs[i+1]
}
}
// Goroutine is finished
wait.Done()
}
wait.Add(3)
// Go for all 3 attribute lists
go f(BoolAttr[:])
go f(NumAttr[:])
go f(StrAttr[:])
// Wait until every goroutine is done
wait.Wait()
// Return the termcap name
return tc
}
// This function takes a path to a terminfo file and reads it in binary
// form to construct the actual TermInfo file.
func readTermInfo(path string) (*TermInfo, error) {
// Open the terminfo file
file, err := os.Open(path)
defer file.Close()
if err != nil {
return nil, err
}
// magic, nameSize, boolSize, nrSNum, nrOffsetsStr, strSize
// Header is composed of the magic 0432 octal number, size of the name
// section, size of the boolean section, the amount of number values,
// the number of offsets of strings, and the size of the string section.
var header [6]int16
// Byte array is used to read in byte values
var byteArray []byte
// Short array is used to read in short values
var shArray []int16
// TermInfo object to store values
var term TermInfo
// Read in the header
err = binary.Read(file, binary.LittleEndian, &header)
if err != nil {
return nil, err
}
// If magic number isn't there or isn't correct, we have the wrong filetype
if header[0] != 0432 {
return nil, errors.New(fmt.Sprintf("Wrong filetype"))
}
// Read in the names
byteArray = make([]byte, header[1])
err = binary.Read(file, binary.LittleEndian, &byteArray)
if err != nil {
return nil, err
}
term.Names = strings.Split(string(byteArray), "|")
// Read in the booleans
byteArray = make([]byte, header[2])
err = binary.Read(file, binary.LittleEndian, &byteArray)
if err != nil {
return nil, err
}
term.boolAttributes = make(map[string]bool)
for i, b := range byteArray {
if b == 1 {
term.boolAttributes[BoolAttr[i*2+1]] = true
}
}
// If the number of bytes read is not even, a byte for alignment is added
if len(byteArray)%2 != 0 {
err = binary.Read(file, binary.LittleEndian, make([]byte, 1))
if err != nil {
return nil, err
}
}
// Read in shorts
shArray = make([]int16, header[3])
err = binary.Read(file, binary.LittleEndian, &shArray)
if err != nil {
return nil, err
}
term.numAttributes = make(map[string]int16)
for i, n := range shArray {
if n != 0377 && n > -1 {
term.numAttributes[NumAttr[i*2+1]] = n
}
}
// Read the offsets into the short array
shArray = make([]int16, header[4])
err = binary.Read(file, binary.LittleEndian, &shArray)
if err != nil {
return nil, err
}
// Read the actual strings in the byte array
byteArray = make([]byte, header[5])
err = binary.Read(file, binary.LittleEndian, &byteArray)
if err != nil {
return nil, err
}
term.strAttributes = make(map[string]string)
// We get an offset, and then iterate until the string is null-terminated
for i, offset := range shArray {
if offset > -1 {
r := offset
for ; byteArray[r] != 0; r++ {
}
term.strAttributes[StrAttr[i*2+1]] = string(byteArray[offset:r])
}
}
return &term, nil
}

View File

@ -1,362 +0,0 @@
// Copyright 2012 Neal van Veen. All rights reserved.
// Usage of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package gotty
import (
"bytes"
"errors"
"fmt"
"regexp"
"strconv"
"strings"
)
var exp = [...]string{
"%%",
"%c",
"%s",
"%p(\\d)",
"%P([A-z])",
"%g([A-z])",
"%'(.)'",
"%{([0-9]+)}",
"%l",
"%\\+|%-|%\\*|%/|%m",
"%&|%\\||%\\^",
"%=|%>|%<",
"%A|%O",
"%!|%~",
"%i",
"%(:[\\ #\\-\\+]{0,4})?(\\d+\\.\\d+|\\d+)?[doxXs]",
"%\\?(.*?);",
}
var regex *regexp.Regexp
var staticVar map[byte]stacker
// Parses the attribute that is received with name attr and parameters params.
func (term *TermInfo) Parse(attr string, params ...interface{}) (string, error) {
// Get the attribute name first.
iface, err := term.GetAttribute(attr)
str, ok := iface.(string)
if err != nil {
return "", err
}
if !ok {
return str, errors.New("Only string capabilities can be parsed.")
}
// Construct the hidden parser struct so we can use a recursive stack based
// parser.
ps := &parser{}
// Dynamic variables only exist in this context.
ps.dynamicVar = make(map[byte]stacker, 26)
ps.parameters = make([]stacker, len(params))
// Convert the parameters to insert them into the parser struct.
for i, x := range params {
ps.parameters[i] = x
}
// Recursively walk and return.
result, err := ps.walk(str)
return result, err
}
// Parses the attribute that is received with name attr and parameters params.
// Only works on full name of a capability that is given, which it uses to
// search for the termcap name.
func (term *TermInfo) ParseName(attr string, params ...interface{}) (string, error) {
tc := GetTermcapName(attr)
return term.Parse(tc, params)
}
// Identify each token in a stack based manner and do the actual parsing.
func (ps *parser) walk(attr string) (string, error) {
// We use a buffer to get the modified string.
var buf bytes.Buffer
// Next, find and identify all tokens by their indices and strings.
tokens := regex.FindAllStringSubmatch(attr, -1)
if len(tokens) == 0 {
return attr, nil
}
indices := regex.FindAllStringIndex(attr, -1)
q := 0 // q counts the matches of one token
// Iterate through the string per character.
for i := 0; i < len(attr); i++ {
// If the current position is an identified token, execute the following
// steps.
if q < len(indices) && i >= indices[q][0] && i < indices[q][1] {
// Switch on token.
switch {
case tokens[q][0][:2] == "%%":
// Literal percentage character.
buf.WriteByte('%')
case tokens[q][0][:2] == "%c":
// Pop a character.
c, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
buf.WriteByte(c.(byte))
case tokens[q][0][:2] == "%s":
// Pop a string.
str, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
if _, ok := str.(string); !ok {
return buf.String(), errors.New("Stack head is not a string")
}
buf.WriteString(str.(string))
case tokens[q][0][:2] == "%p":
// Push a parameter on the stack.
index, err := strconv.ParseInt(tokens[q][1], 10, 8)
index--
if err != nil {
return buf.String(), err
}
if int(index) >= len(ps.parameters) {
return buf.String(), errors.New("Parameters index out of bound")
}
ps.st.push(ps.parameters[index])
case tokens[q][0][:2] == "%P":
// Pop a variable from the stack as a dynamic or static variable.
val, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
index := tokens[q][2]
if len(index) > 1 {
errorStr := fmt.Sprintf("%s is not a valid dynamic variables index",
index)
return buf.String(), errors.New(errorStr)
}
// Specify either dynamic or static.
if index[0] >= 'a' && index[0] <= 'z' {
ps.dynamicVar[index[0]] = val
} else if index[0] >= 'A' && index[0] <= 'Z' {
staticVar[index[0]] = val
}
case tokens[q][0][:2] == "%g":
// Push a variable from the stack as a dynamic or static variable.
index := tokens[q][3]
if len(index) > 1 {
errorStr := fmt.Sprintf("%s is not a valid static variables index",
index)
return buf.String(), errors.New(errorStr)
}
var val stacker
if index[0] >= 'a' && index[0] <= 'z' {
val = ps.dynamicVar[index[0]]
} else if index[0] >= 'A' && index[0] <= 'Z' {
val = staticVar[index[0]]
}
ps.st.push(val)
case tokens[q][0][:2] == "%'":
// Push a character constant.
con := tokens[q][4]
if len(con) > 1 {
errorStr := fmt.Sprintf("%s is not a valid character constant", con)
return buf.String(), errors.New(errorStr)
}
ps.st.push(con[0])
case tokens[q][0][:2] == "%{":
// Push an integer constant.
con, err := strconv.ParseInt(tokens[q][5], 10, 32)
if err != nil {
return buf.String(), err
}
ps.st.push(con)
case tokens[q][0][:2] == "%l":
// Push the length of the string that is popped from the stack.
popStr, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
if _, ok := popStr.(string); !ok {
errStr := fmt.Sprintf("Stack head is not a string")
return buf.String(), errors.New(errStr)
}
ps.st.push(len(popStr.(string)))
case tokens[q][0][:2] == "%?":
// If-then-else construct. First, the whole string is identified and
// then inside this substring, we can specify which parts to switch on.
ifReg, _ := regexp.Compile("%\\?(.*)%t(.*)%e(.*);|%\\?(.*)%t(.*);")
ifTokens := ifReg.FindStringSubmatch(tokens[q][0])
var (
ifStr string
err error
)
// Parse the if-part to determine if-else.
if len(ifTokens[1]) > 0 {
ifStr, err = ps.walk(ifTokens[1])
} else { // else
ifStr, err = ps.walk(ifTokens[4])
}
// Return any errors
if err != nil {
return buf.String(), err
} else if len(ifStr) > 0 {
// Self-defined limitation, not sure if this is correct, but didn't
// seem like it.
return buf.String(), errors.New("If-clause cannot print statements")
}
var thenStr string
// Pop the first value that is set by parsing the if-clause.
choose, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
// Switch to if or else.
if choose.(int) == 0 && len(ifTokens[1]) > 0 {
thenStr, err = ps.walk(ifTokens[3])
} else if choose.(int) != 0 {
if len(ifTokens[1]) > 0 {
thenStr, err = ps.walk(ifTokens[2])
} else {
thenStr, err = ps.walk(ifTokens[5])
}
}
if err != nil {
return buf.String(), err
}
buf.WriteString(thenStr)
case tokens[q][0][len(tokens[q][0])-1] == 'd': // Fallthrough for printing
fallthrough
case tokens[q][0][len(tokens[q][0])-1] == 'o': // digits.
fallthrough
case tokens[q][0][len(tokens[q][0])-1] == 'x':
fallthrough
case tokens[q][0][len(tokens[q][0])-1] == 'X':
fallthrough
case tokens[q][0][len(tokens[q][0])-1] == 's':
token := tokens[q][0]
// Remove the : that comes before a flag.
if token[1] == ':' {
token = token[:1] + token[2:]
}
digit, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
// The rest is determined like the normal formatted prints.
digitStr := fmt.Sprintf(token, digit.(int))
buf.WriteString(digitStr)
case tokens[q][0][:2] == "%i":
// Increment the parameters by one.
if len(ps.parameters) < 2 {
return buf.String(), errors.New("Not enough parameters to increment.")
}
val1, val2 := ps.parameters[0].(int), ps.parameters[1].(int)
val1++
val2++
ps.parameters[0], ps.parameters[1] = val1, val2
default:
// The rest of the tokens is a special case, where two values are
// popped and then operated on by the token that comes after them.
op1, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
op2, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
var result stacker
switch tokens[q][0][:2] {
case "%+":
// Addition
result = op2.(int) + op1.(int)
case "%-":
// Subtraction
result = op2.(int) - op1.(int)
case "%*":
// Multiplication
result = op2.(int) * op1.(int)
case "%/":
// Division
result = op2.(int) / op1.(int)
case "%m":
// Modulo
result = op2.(int) % op1.(int)
case "%&":
// Bitwise AND
result = op2.(int) & op1.(int)
case "%|":
// Bitwise OR
result = op2.(int) | op1.(int)
case "%^":
// Bitwise XOR
result = op2.(int) ^ op1.(int)
case "%=":
// Equals
result = op2 == op1
case "%>":
// Greater-than
result = op2.(int) > op1.(int)
case "%<":
// Lesser-than
result = op2.(int) < op1.(int)
case "%A":
// Logical AND
result = op2.(bool) && op1.(bool)
case "%O":
// Logical OR
result = op2.(bool) || op1.(bool)
case "%!":
// Logical complement
result = !op1.(bool)
case "%~":
// Bitwise complement
result = ^(op1.(int))
}
ps.st.push(result)
}
i = indices[q][1] - 1
q++
} else {
// We are not "inside" a token, so just skip until the end or the next
// token, and add all characters to the buffer.
j := i
if q != len(indices) {
for !(j >= indices[q][0] && j < indices[q][1]) {
j++
}
} else {
j = len(attr)
}
buf.WriteString(string(attr[i:j]))
i = j
}
}
// Return the buffer as a string.
return buf.String(), nil
}
// Push a stacker-value onto the stack.
func (st *stack) push(s stacker) {
*st = append(*st, s)
}
// Pop a stacker-value from the stack.
func (st *stack) pop() (stacker, error) {
if len(*st) == 0 {
return nil, errors.New("Stack is empty.")
}
newStack := make(stack, len(*st)-1)
val := (*st)[len(*st)-1]
copy(newStack, (*st)[:len(*st)-1])
*st = newStack
return val, nil
}
// Initialize regexes and the static vars (that don't get changed between
// calls.
func init() {
// Initialize the main regex.
expStr := strings.Join(exp[:], "|")
regex, _ = regexp.Compile(expStr)
// Initialize the static variables.
staticVar = make(map[byte]stacker, 26)
}

View File

@ -1,23 +0,0 @@
// Copyright 2012 Neal van Veen. All rights reserved.
// Usage of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package gotty
type TermInfo struct {
boolAttributes map[string]bool
numAttributes map[string]int16
strAttributes map[string]string
// The various names of the TermInfo file.
Names []string
}
type stacker interface {
}
type stack []stacker
type parser struct {
st stack
parameters []stacker
dynamicVar map[byte]stacker
}

View File

@ -142,6 +142,16 @@ type PluginConfig struct {
// Log line longer than the limit will be split into multiple lines. Non-positive // Log line longer than the limit will be split into multiple lines. Non-positive
// value means no limit. // value means no limit.
MaxContainerLogLineSize int `toml:"max_container_log_line_size" json:"maxContainerLogSize"` MaxContainerLogLineSize int `toml:"max_container_log_line_size" json:"maxContainerLogSize"`
// DisableCgroup indicates to disable the cgroup support.
// This is useful when the containerd does not have permission to access cgroup.
DisableCgroup bool `toml:"disable_cgroup" json:"disableCgroup"`
// DisableApparmor indicates to disable the apparmor support.
// This is useful when the containerd does not have permission to access Apparmor.
DisableApparmor bool `toml:"disable_apparmor" json:"disableApparmor"`
// RestrictOOMScoreAdj indicates to limit the lower bound of OOMScoreAdj to the containerd's
// current OOMScoreADj.
// This is useful when the containerd does not have permission to decrease OOMScoreAdj.
RestrictOOMScoreAdj bool `toml:"restrict_oom_score_adj" json:"restrictOOMScoreAdj"`
} }
// X509KeyPairStreaming contains the x509 configuration for streaming // X509KeyPairStreaming contains the x509 configuration for streaming

View File

@ -416,12 +416,18 @@ func (c *criService) generateContainerSpec(id string, sandboxID string, sandboxP
g.SetRootReadonly(securityContext.GetReadonlyRootfs()) g.SetRootReadonly(securityContext.GetReadonlyRootfs())
setOCILinuxResource(&g, config.GetLinux().GetResources()) if c.config.DisableCgroup {
g.SetLinuxCgroupsPath("")
if sandboxConfig.GetLinux().GetCgroupParent() != "" { } else {
cgroupsPath := getCgroupsPath(sandboxConfig.GetLinux().GetCgroupParent(), id, setOCILinuxResourceCgroup(&g, config.GetLinux().GetResources())
c.config.SystemdCgroup) if sandboxConfig.GetLinux().GetCgroupParent() != "" {
g.SetLinuxCgroupsPath(cgroupsPath) cgroupsPath := getCgroupsPath(sandboxConfig.GetLinux().GetCgroupParent(), id,
c.config.SystemdCgroup)
g.SetLinuxCgroupsPath(cgroupsPath)
}
}
if err := setOCILinuxResourceOOMScoreAdj(&g, config.GetLinux().GetResources(), c.config.RestrictOOMScoreAdj); err != nil {
return nil, err
} }
// Set namespaces, share namespace with sandbox container. // Set namespaces, share namespace with sandbox container.
@ -759,8 +765,8 @@ func setOCIBindMountsPrivileged(g *generator) {
spec.Linux.MaskedPaths = nil spec.Linux.MaskedPaths = nil
} }
// setOCILinuxResource set container resource limit. // setOCILinuxResourceCgroup set container cgroup resource limit.
func setOCILinuxResource(g *generator, resources *runtime.LinuxContainerResources) { func setOCILinuxResourceCgroup(g *generator, resources *runtime.LinuxContainerResources) {
if resources == nil { if resources == nil {
return return
} }
@ -768,11 +774,28 @@ func setOCILinuxResource(g *generator, resources *runtime.LinuxContainerResource
g.SetLinuxResourcesCPUQuota(resources.GetCpuQuota()) g.SetLinuxResourcesCPUQuota(resources.GetCpuQuota())
g.SetLinuxResourcesCPUShares(uint64(resources.GetCpuShares())) g.SetLinuxResourcesCPUShares(uint64(resources.GetCpuShares()))
g.SetLinuxResourcesMemoryLimit(resources.GetMemoryLimitInBytes()) g.SetLinuxResourcesMemoryLimit(resources.GetMemoryLimitInBytes())
g.SetProcessOOMScoreAdj(int(resources.GetOomScoreAdj()))
g.SetLinuxResourcesCPUCpus(resources.GetCpusetCpus()) g.SetLinuxResourcesCPUCpus(resources.GetCpusetCpus())
g.SetLinuxResourcesCPUMems(resources.GetCpusetMems()) g.SetLinuxResourcesCPUMems(resources.GetCpusetMems())
} }
// setOCILinuxResourceOOMScoreAdj set container OOMScoreAdj resource limit.
func setOCILinuxResourceOOMScoreAdj(g *generator, resources *runtime.LinuxContainerResources, restrictOOMScoreAdjFlag bool) error {
if resources == nil {
return nil
}
adj := int(resources.GetOomScoreAdj())
if restrictOOMScoreAdjFlag {
var err error
adj, err = restrictOOMScoreAdj(adj)
if err != nil {
return err
}
}
g.SetProcessOOMScoreAdj(adj)
return nil
}
// getOCICapabilitiesList returns a list of all available capabilities. // getOCICapabilitiesList returns a list of all available capabilities.
func getOCICapabilitiesList() []string { func getOCICapabilitiesList() []string {
var caps []string var caps []string

View File

@ -18,6 +18,7 @@ package server
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
@ -143,9 +144,9 @@ const (
// generated is unique as long as sandbox metadata is unique. // generated is unique as long as sandbox metadata is unique.
func makeSandboxName(s *runtime.PodSandboxMetadata) string { func makeSandboxName(s *runtime.PodSandboxMetadata) string {
return strings.Join([]string{ return strings.Join([]string{
s.Name, // 0 s.Name, // 0
s.Namespace, // 1 s.Namespace, // 1
s.Uid, // 2 s.Uid, // 2
fmt.Sprintf("%d", s.Attempt), // 3 fmt.Sprintf("%d", s.Attempt), // 3
}, nameDelimiter) }, nameDelimiter)
} }
@ -155,10 +156,10 @@ func makeSandboxName(s *runtime.PodSandboxMetadata) string {
// unique. // unique.
func makeContainerName(c *runtime.ContainerMetadata, s *runtime.PodSandboxMetadata) string { func makeContainerName(c *runtime.ContainerMetadata, s *runtime.PodSandboxMetadata) string {
return strings.Join([]string{ return strings.Join([]string{
c.Name, // 0 c.Name, // 0
s.Name, // 1: pod name s.Name, // 1: pod name
s.Namespace, // 2: pod namespace s.Namespace, // 2: pod namespace
s.Uid, // 3: pod uid s.Uid, // 3: pod uid
fmt.Sprintf("%d", c.Attempt), // 4 fmt.Sprintf("%d", c.Attempt), // 4
}, nameDelimiter) }, nameDelimiter)
} }
@ -603,3 +604,27 @@ func getTaskStatus(ctx context.Context, task containerd.Task) (containerd.Status
} }
return status, nil return status, nil
} }
func getCurrentOOMScoreAdj() (int, error) {
b, err := ioutil.ReadFile("/proc/self/oom_score_adj")
if err != nil {
return 0, errors.Wrap(err, "could not get the daemon oom_score_adj")
}
s := strings.TrimSpace(string(b))
i, err := strconv.Atoi(s)
if err != nil {
return 0, errors.Wrap(err, "could not get the daemon oom_score_adj")
}
return i, nil
}
func restrictOOMScoreAdj(preferredOOMScoreAdj int) (int, error) {
currentOOMScoreAdj, err := getCurrentOOMScoreAdj()
if err != nil {
return preferredOOMScoreAdj, err
}
if preferredOOMScoreAdj < currentOOMScoreAdj {
return currentOOMScoreAdj, nil
}
return preferredOOMScoreAdj, nil
}

View File

@ -371,10 +371,14 @@ func (c *criService) generateSandboxContainerSpec(id string, config *runtime.Pod
// TODO(random-liu): [P2] Consider whether to add labels and annotations to the container. // TODO(random-liu): [P2] Consider whether to add labels and annotations to the container.
// Set cgroups parent. // Set cgroups parent.
if config.GetLinux().GetCgroupParent() != "" { if c.config.DisableCgroup {
cgroupsPath := getCgroupsPath(config.GetLinux().GetCgroupParent(), id, g.SetLinuxCgroupsPath("")
c.config.SystemdCgroup) } else {
g.SetLinuxCgroupsPath(cgroupsPath) if config.GetLinux().GetCgroupParent() != "" {
cgroupsPath := getCgroupsPath(config.GetLinux().GetCgroupParent(), id,
c.config.SystemdCgroup)
g.SetLinuxCgroupsPath(cgroupsPath)
}
} }
// When cgroup parent is not set, containerd-shim will create container in a child cgroup // When cgroup parent is not set, containerd-shim will create container in a child cgroup
// of the cgroup itself is in. // of the cgroup itself is in.
@ -431,8 +435,17 @@ func (c *criService) generateSandboxContainerSpec(id string, config *runtime.Pod
// Note: LinuxSandboxSecurityContext does not currently provide an apparmor profile // Note: LinuxSandboxSecurityContext does not currently provide an apparmor profile
g.SetLinuxResourcesCPUShares(uint64(defaultSandboxCPUshares)) if !c.config.DisableCgroup {
g.SetProcessOOMScoreAdj(int(defaultSandboxOOMAdj)) g.SetLinuxResourcesCPUShares(uint64(defaultSandboxCPUshares))
}
adj := int(defaultSandboxOOMAdj)
if c.config.RestrictOOMScoreAdj {
adj, err = restrictOOMScoreAdj(adj)
if err != nil {
return nil, err
}
}
g.SetProcessOOMScoreAdj(adj)
g.AddAnnotation(annotations.ContainerType, annotations.ContainerTypeSandbox) g.AddAnnotation(annotations.ContainerType, annotations.ContainerTypeSandbox)
g.AddAnnotation(annotations.SandboxID, id) g.AddAnnotation(annotations.SandboxID, id)

View File

@ -28,6 +28,7 @@ import (
cni "github.com/containerd/go-cni" cni "github.com/containerd/go-cni"
runcapparmor "github.com/opencontainers/runc/libcontainer/apparmor" runcapparmor "github.com/opencontainers/runc/libcontainer/apparmor"
runcseccomp "github.com/opencontainers/runc/libcontainer/seccomp" runcseccomp "github.com/opencontainers/runc/libcontainer/seccomp"
runcsystem "github.com/opencontainers/runc/libcontainer/system"
"github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -108,7 +109,7 @@ func NewCRIService(config criconfig.Config, client *containerd.Client) (CRIServi
c := &criService{ c := &criService{
config: config, config: config,
client: client, client: client,
apparmorEnabled: runcapparmor.IsEnabled(), apparmorEnabled: runcapparmor.IsEnabled() && !config.DisableApparmor,
seccompEnabled: runcseccomp.IsEnabled(), seccompEnabled: runcseccomp.IsEnabled(),
os: osinterface.RealOS{}, os: osinterface.RealOS{},
sandboxStore: sandboxstore.NewStore(), sandboxStore: sandboxstore.NewStore(),
@ -120,6 +121,12 @@ func NewCRIService(config criconfig.Config, client *containerd.Client) (CRIServi
initialized: atomic.NewBool(false), initialized: atomic.NewBool(false),
} }
if runcsystem.RunningInUserNS() {
if !(config.DisableCgroup && !c.apparmorEnabled && config.RestrictOOMScoreAdj) {
logrus.Warn("Running containerd in a user namespace typically requires disable_cgroup, disable_apparmor, restrict_oom_score_adj set to be true")
}
}
if c.config.EnableSelinux { if c.config.EnableSelinux {
if !selinux.GetEnabled() { if !selinux.GetEnabled() {
logrus.Warn("Selinux is not supported") logrus.Warn("Selinux is not supported")

View File

@ -0,0 +1,267 @@
package errcode
import (
"encoding/json"
"fmt"
"strings"
)
// ErrorCoder is the base interface for ErrorCode and Error allowing
// users of each to just call ErrorCode to get the real ID of each
type ErrorCoder interface {
ErrorCode() ErrorCode
}
// ErrorCode represents the error type. The errors are serialized via strings
// and the integer format may change and should *never* be exported.
type ErrorCode int
var _ error = ErrorCode(0)
// ErrorCode just returns itself
func (ec ErrorCode) ErrorCode() ErrorCode {
return ec
}
// Error returns the ID/Value
func (ec ErrorCode) Error() string {
// NOTE(stevvooe): Cannot use message here since it may have unpopulated args.
return strings.ToLower(strings.Replace(ec.String(), "_", " ", -1))
}
// Descriptor returns the descriptor for the error code.
func (ec ErrorCode) Descriptor() ErrorDescriptor {
d, ok := errorCodeToDescriptors[ec]
if !ok {
return ErrorCodeUnknown.Descriptor()
}
return d
}
// String returns the canonical identifier for this error code.
func (ec ErrorCode) String() string {
return ec.Descriptor().Value
}
// Message returned the human-readable error message for this error code.
func (ec ErrorCode) Message() string {
return ec.Descriptor().Message
}
// MarshalText encodes the receiver into UTF-8-encoded text and returns the
// result.
func (ec ErrorCode) MarshalText() (text []byte, err error) {
return []byte(ec.String()), nil
}
// UnmarshalText decodes the form generated by MarshalText.
func (ec *ErrorCode) UnmarshalText(text []byte) error {
desc, ok := idToDescriptors[string(text)]
if !ok {
desc = ErrorCodeUnknown.Descriptor()
}
*ec = desc.Code
return nil
}
// WithMessage creates a new Error struct based on the passed-in info and
// overrides the Message property.
func (ec ErrorCode) WithMessage(message string) Error {
return Error{
Code: ec,
Message: message,
}
}
// WithDetail creates a new Error struct based on the passed-in info and
// set the Detail property appropriately
func (ec ErrorCode) WithDetail(detail interface{}) Error {
return Error{
Code: ec,
Message: ec.Message(),
}.WithDetail(detail)
}
// WithArgs creates a new Error struct and sets the Args slice
func (ec ErrorCode) WithArgs(args ...interface{}) Error {
return Error{
Code: ec,
Message: ec.Message(),
}.WithArgs(args...)
}
// Error provides a wrapper around ErrorCode with extra Details provided.
type Error struct {
Code ErrorCode `json:"code"`
Message string `json:"message"`
Detail interface{} `json:"detail,omitempty"`
// TODO(duglin): See if we need an "args" property so we can do the
// variable substitution right before showing the message to the user
}
var _ error = Error{}
// ErrorCode returns the ID/Value of this Error
func (e Error) ErrorCode() ErrorCode {
return e.Code
}
// Error returns a human readable representation of the error.
func (e Error) Error() string {
return fmt.Sprintf("%s: %s", e.Code.Error(), e.Message)
}
// WithDetail will return a new Error, based on the current one, but with
// some Detail info added
func (e Error) WithDetail(detail interface{}) Error {
return Error{
Code: e.Code,
Message: e.Message,
Detail: detail,
}
}
// WithArgs uses the passed-in list of interface{} as the substitution
// variables in the Error's Message string, but returns a new Error
func (e Error) WithArgs(args ...interface{}) Error {
return Error{
Code: e.Code,
Message: fmt.Sprintf(e.Code.Message(), args...),
Detail: e.Detail,
}
}
// ErrorDescriptor provides relevant information about a given error code.
type ErrorDescriptor struct {
// Code is the error code that this descriptor describes.
Code ErrorCode
// Value provides a unique, string key, often captilized with
// underscores, to identify the error code. This value is used as the
// keyed value when serializing api errors.
Value string
// Message is a short, human readable decription of the error condition
// included in API responses.
Message string
// Description provides a complete account of the errors purpose, suitable
// for use in documentation.
Description string
// HTTPStatusCode provides the http status code that is associated with
// this error condition.
HTTPStatusCode int
}
// ParseErrorCode returns the value by the string error code.
// `ErrorCodeUnknown` will be returned if the error is not known.
func ParseErrorCode(value string) ErrorCode {
ed, ok := idToDescriptors[value]
if ok {
return ed.Code
}
return ErrorCodeUnknown
}
// Errors provides the envelope for multiple errors and a few sugar methods
// for use within the application.
type Errors []error
var _ error = Errors{}
func (errs Errors) Error() string {
switch len(errs) {
case 0:
return "<nil>"
case 1:
return errs[0].Error()
default:
msg := "errors:\n"
for _, err := range errs {
msg += err.Error() + "\n"
}
return msg
}
}
// Len returns the current number of errors.
func (errs Errors) Len() int {
return len(errs)
}
// MarshalJSON converts slice of error, ErrorCode or Error into a
// slice of Error - then serializes
func (errs Errors) MarshalJSON() ([]byte, error) {
var tmpErrs struct {
Errors []Error `json:"errors,omitempty"`
}
for _, daErr := range errs {
var err Error
switch daErr := daErr.(type) {
case ErrorCode:
err = daErr.WithDetail(nil)
case Error:
err = daErr
default:
err = ErrorCodeUnknown.WithDetail(daErr)
}
// If the Error struct was setup and they forgot to set the
// Message field (meaning its "") then grab it from the ErrCode
msg := err.Message
if msg == "" {
msg = err.Code.Message()
}
tmpErrs.Errors = append(tmpErrs.Errors, Error{
Code: err.Code,
Message: msg,
Detail: err.Detail,
})
}
return json.Marshal(tmpErrs)
}
// UnmarshalJSON deserializes []Error and then converts it into slice of
// Error or ErrorCode
func (errs *Errors) UnmarshalJSON(data []byte) error {
var tmpErrs struct {
Errors []Error
}
if err := json.Unmarshal(data, &tmpErrs); err != nil {
return err
}
var newErrs Errors
for _, daErr := range tmpErrs.Errors {
// If Message is empty or exactly matches the Code's message string
// then just use the Code, no need for a full Error struct
if daErr.Detail == nil && (daErr.Message == "" || daErr.Message == daErr.Code.Message()) {
// Error's w/o details get converted to ErrorCode
newErrs = append(newErrs, daErr.Code)
} else {
// Error's w/ details are untouched
newErrs = append(newErrs, Error{
Code: daErr.Code,
Message: daErr.Message,
Detail: daErr.Detail,
})
}
}
*errs = newErrs
return nil
}

View File

@ -0,0 +1,40 @@
package errcode
import (
"encoding/json"
"net/http"
)
// ServeJSON attempts to serve the errcode in a JSON envelope. It marshals err
// and sets the content-type header to 'application/json'. It will handle
// ErrorCoder and Errors, and if necessary will create an envelope.
func ServeJSON(w http.ResponseWriter, err error) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
var sc int
switch errs := err.(type) {
case Errors:
if len(errs) < 1 {
break
}
if err, ok := errs[0].(ErrorCoder); ok {
sc = err.ErrorCode().Descriptor().HTTPStatusCode
}
case ErrorCoder:
sc = errs.ErrorCode().Descriptor().HTTPStatusCode
err = Errors{err} // create an envelope.
default:
// We just have an unhandled error type, so just place in an envelope
// and move along.
err = Errors{err}
}
if sc == 0 {
sc = http.StatusInternalServerError
}
w.WriteHeader(sc)
return json.NewEncoder(w).Encode(err)
}

View File

@ -0,0 +1,138 @@
package errcode
import (
"fmt"
"net/http"
"sort"
"sync"
)
var (
errorCodeToDescriptors = map[ErrorCode]ErrorDescriptor{}
idToDescriptors = map[string]ErrorDescriptor{}
groupToDescriptors = map[string][]ErrorDescriptor{}
)
var (
// ErrorCodeUnknown is a generic error that can be used as a last
// resort if there is no situation-specific error message that can be used
ErrorCodeUnknown = Register("errcode", ErrorDescriptor{
Value: "UNKNOWN",
Message: "unknown error",
Description: `Generic error returned when the error does not have an
API classification.`,
HTTPStatusCode: http.StatusInternalServerError,
})
// ErrorCodeUnsupported is returned when an operation is not supported.
ErrorCodeUnsupported = Register("errcode", ErrorDescriptor{
Value: "UNSUPPORTED",
Message: "The operation is unsupported.",
Description: `The operation was unsupported due to a missing
implementation or invalid set of parameters.`,
HTTPStatusCode: http.StatusMethodNotAllowed,
})
// ErrorCodeUnauthorized is returned if a request requires
// authentication.
ErrorCodeUnauthorized = Register("errcode", ErrorDescriptor{
Value: "UNAUTHORIZED",
Message: "authentication required",
Description: `The access controller was unable to authenticate
the client. Often this will be accompanied by a
Www-Authenticate HTTP response header indicating how to
authenticate.`,
HTTPStatusCode: http.StatusUnauthorized,
})
// ErrorCodeDenied is returned if a client does not have sufficient
// permission to perform an action.
ErrorCodeDenied = Register("errcode", ErrorDescriptor{
Value: "DENIED",
Message: "requested access to the resource is denied",
Description: `The access controller denied access for the
operation on a resource.`,
HTTPStatusCode: http.StatusForbidden,
})
// ErrorCodeUnavailable provides a common error to report unavailability
// of a service or endpoint.
ErrorCodeUnavailable = Register("errcode", ErrorDescriptor{
Value: "UNAVAILABLE",
Message: "service unavailable",
Description: "Returned when a service is not available",
HTTPStatusCode: http.StatusServiceUnavailable,
})
// ErrorCodeTooManyRequests is returned if a client attempts too many
// times to contact a service endpoint.
ErrorCodeTooManyRequests = Register("errcode", ErrorDescriptor{
Value: "TOOMANYREQUESTS",
Message: "too many requests",
Description: `Returned when a client attempts to contact a
service too many times`,
HTTPStatusCode: http.StatusTooManyRequests,
})
)
var nextCode = 1000
var registerLock sync.Mutex
// Register will make the passed-in error known to the environment and
// return a new ErrorCode
func Register(group string, descriptor ErrorDescriptor) ErrorCode {
registerLock.Lock()
defer registerLock.Unlock()
descriptor.Code = ErrorCode(nextCode)
if _, ok := idToDescriptors[descriptor.Value]; ok {
panic(fmt.Sprintf("ErrorValue %q is already registered", descriptor.Value))
}
if _, ok := errorCodeToDescriptors[descriptor.Code]; ok {
panic(fmt.Sprintf("ErrorCode %v is already registered", descriptor.Code))
}
groupToDescriptors[group] = append(groupToDescriptors[group], descriptor)
errorCodeToDescriptors[descriptor.Code] = descriptor
idToDescriptors[descriptor.Value] = descriptor
nextCode++
return descriptor.Code
}
type byValue []ErrorDescriptor
func (a byValue) Len() int { return len(a) }
func (a byValue) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byValue) Less(i, j int) bool { return a[i].Value < a[j].Value }
// GetGroupNames returns the list of Error group names that are registered
func GetGroupNames() []string {
keys := []string{}
for k := range groupToDescriptors {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}
// GetErrorCodeGroup returns the named group of error descriptors
func GetErrorCodeGroup(name string) []ErrorDescriptor {
desc := groupToDescriptors[name]
sort.Sort(byValue(desc))
return desc
}
// GetErrorAllDescriptors returns a slice of all ErrorDescriptors that are
// registered, irrespective of what group they're in
func GetErrorAllDescriptors() []ErrorDescriptor {
result := []ErrorDescriptor{}
for _, group := range GetGroupNames() {
result = append(result, GetErrorCodeGroup(group)...)
}
sort.Sort(byValue(result))
return result
}

View File

@ -7,7 +7,9 @@ curators:
- ehazlett - ehazlett
- fntlnz - fntlnz
- gianarb - gianarb
- kolyshkin
- mgoelzer - mgoelzer
- olljanat
- programmerq - programmerq
- rheinwein - rheinwein
- ripcurld0 - ripcurld0
@ -15,3 +17,5 @@ curators:
features: features:
- comments - comments
- pr_description_required

View File

@ -35,6 +35,8 @@ Allen Sun <allensun.shl@alibaba-inc.com> <allen.sun@daocloud.io>
Allen Sun <allensun.shl@alibaba-inc.com> <shlallen1990@gmail.com> Allen Sun <allensun.shl@alibaba-inc.com> <shlallen1990@gmail.com>
Andrew Weiss <andrew.weiss@docker.com> <andrew.weiss@microsoft.com> Andrew Weiss <andrew.weiss@docker.com> <andrew.weiss@microsoft.com>
Andrew Weiss <andrew.weiss@docker.com> <andrew.weiss@outlook.com> Andrew Weiss <andrew.weiss@docker.com> <andrew.weiss@outlook.com>
Andrey Kolomentsev <andrey.kolomentsev@docker.com>
Andrey Kolomentsev <andrey.kolomentsev@docker.com> <andrey.kolomentsev@gmail.com>
André Martins <aanm90@gmail.com> <martins@noironetworks.com> André Martins <aanm90@gmail.com> <martins@noironetworks.com>
Andy Rothfusz <github@developersupport.net> <github@metaliveblog.com> Andy Rothfusz <github@developersupport.net> <github@metaliveblog.com>
Andy Smith <github@anarkystic.com> Andy Smith <github@anarkystic.com>
@ -55,9 +57,11 @@ Ben Bonnefoy <frenchben@docker.com>
Ben Golub <ben.golub@dotcloud.com> Ben Golub <ben.golub@dotcloud.com>
Ben Toews <mastahyeti@gmail.com> <mastahyeti@users.noreply.github.com> Ben Toews <mastahyeti@gmail.com> <mastahyeti@users.noreply.github.com>
Benoit Chesneau <bchesneau@gmail.com> Benoit Chesneau <bchesneau@gmail.com>
Bevisy Zhang <binbin36520@gmail.com>
Bhiraj Butala <abhiraj.butala@gmail.com> Bhiraj Butala <abhiraj.butala@gmail.com>
Bhumika Bayani <bhumikabayani@gmail.com> Bhumika Bayani <bhumikabayani@gmail.com>
Bilal Amarni <bilal.amarni@gmail.com> <bamarni@users.noreply.github.com> Bilal Amarni <bilal.amarni@gmail.com> <bamarni@users.noreply.github.com>
Bily Zhang <xcoder@tenxcloud.com>
Bill Wang <ozbillwang@gmail.com> <SydOps@users.noreply.github.com> Bill Wang <ozbillwang@gmail.com> <SydOps@users.noreply.github.com>
Bin Liu <liubin0329@gmail.com> Bin Liu <liubin0329@gmail.com>
Bin Liu <liubin0329@gmail.com> <liubin0329@users.noreply.github.com> Bin Liu <liubin0329@gmail.com> <liubin0329@users.noreply.github.com>
@ -77,6 +81,7 @@ Chen Chuanliang <chen.chuanliang@zte.com.cn>
Chen Mingjie <chenmingjie0828@163.com> Chen Mingjie <chenmingjie0828@163.com>
Chen Qiu <cheney-90@hotmail.com> Chen Qiu <cheney-90@hotmail.com>
Chen Qiu <cheney-90@hotmail.com> <21321229@zju.edu.cn> Chen Qiu <cheney-90@hotmail.com> <21321229@zju.edu.cn>
Chengfei Shang <cfshang@alauda.io>
Chris Dias <cdias@microsoft.com> Chris Dias <cdias@microsoft.com>
Chris McKinnel <chris.mckinnel@tangentlabs.co.uk> Chris McKinnel <chris.mckinnel@tangentlabs.co.uk>
Christopher Biscardi <biscarch@sketcht.com> Christopher Biscardi <biscarch@sketcht.com>
@ -97,6 +102,7 @@ Daniel Garcia <daniel@danielgarcia.info>
Daniel Gasienica <daniel@gasienica.ch> <dgasienica@zynga.com> Daniel Gasienica <daniel@gasienica.ch> <dgasienica@zynga.com>
Daniel Goosen <daniel.goosen@surveysampling.com> <djgoosen@users.noreply.github.com> Daniel Goosen <daniel.goosen@surveysampling.com> <djgoosen@users.noreply.github.com>
Daniel Grunwell <mwgrunny@gmail.com> Daniel Grunwell <mwgrunny@gmail.com>
Daniel Hiltgen <daniel.hiltgen@docker.com> <dhiltgen@users.noreply.github.com>
Daniel J Walsh <dwalsh@redhat.com> Daniel J Walsh <dwalsh@redhat.com>
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com> <daniel@dotcloud.com> Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com> <daniel@dotcloud.com>
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com> <mzdaniel@glidelink.net> Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com> <mzdaniel@glidelink.net>
@ -104,6 +110,7 @@ Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com> <root@vagrant-ubuntu-12.10.vagr
Daniel Nephin <dnephin@docker.com> <dnephin@gmail.com> Daniel Nephin <dnephin@docker.com> <dnephin@gmail.com>
Daniel Norberg <dano@spotify.com> <daniel.norberg@gmail.com> Daniel Norberg <dano@spotify.com> <daniel.norberg@gmail.com>
Daniel Watkins <daniel@daniel-watkins.co.uk> Daniel Watkins <daniel@daniel-watkins.co.uk>
Daniel Zhang <jmzwcn@gmail.com>
Danny Yates <danny@codeaholics.org> <Danny.Yates@mailonline.co.uk> Danny Yates <danny@codeaholics.org> <Danny.Yates@mailonline.co.uk>
Darren Shepherd <darren.s.shepherd@gmail.com> <darren@rancher.com> Darren Shepherd <darren.s.shepherd@gmail.com> <darren@rancher.com>
Dattatraya Kumbhar <dattatraya.kumbhar@gslab.com> Dattatraya Kumbhar <dattatraya.kumbhar@gslab.com>
@ -149,6 +156,7 @@ Frederick F. Kautz IV <fkautz@redhat.com> <fkautz@alumni.cmu.edu>
Gabriel Nicolas Avellaneda <avellaneda.gabriel@gmail.com> Gabriel Nicolas Avellaneda <avellaneda.gabriel@gmail.com>
Gaetan de Villele <gdevillele@gmail.com> Gaetan de Villele <gdevillele@gmail.com>
Gang Qiao <qiaohai8866@gmail.com> <1373319223@qq.com> Gang Qiao <qiaohai8866@gmail.com> <1373319223@qq.com>
Geon Kim <geon0250@gmail.com>
George Kontridze <george@bugsnag.com> George Kontridze <george@bugsnag.com>
Gerwim Feiken <g.feiken@tfe.nl> <gerwim@gmail.com> Gerwim Feiken <g.feiken@tfe.nl> <gerwim@gmail.com>
Giampaolo Mancini <giampaolo@trampolineup.com> Giampaolo Mancini <giampaolo@trampolineup.com>
@ -175,6 +183,7 @@ Harry Zhang <harryz@hyper.sh> <resouer@gmail.com>
Harry Zhang <resouer@163.com> Harry Zhang <resouer@163.com>
Harshal Patil <harshal.patil@in.ibm.com> <harche@users.noreply.github.com> Harshal Patil <harshal.patil@in.ibm.com> <harche@users.noreply.github.com>
Helen Xie <chenjg@harmonycloud.cn> Helen Xie <chenjg@harmonycloud.cn>
Hiroyuki Sasagawa <hs19870702@gmail.com>
Hollie Teal <hollie@docker.com> Hollie Teal <hollie@docker.com>
Hollie Teal <hollie@docker.com> <hollie.teal@docker.com> Hollie Teal <hollie@docker.com> <hollie.teal@docker.com>
Hollie Teal <hollie@docker.com> <hollietealok@users.noreply.github.com> Hollie Teal <hollie@docker.com> <hollietealok@users.noreply.github.com>
@ -183,26 +192,32 @@ Huu Nguyen <huu@prismskylabs.com> <whoshuu@gmail.com>
Hyzhou Zhy <hyzhou.zhy@alibaba-inc.com> Hyzhou Zhy <hyzhou.zhy@alibaba-inc.com>
Hyzhou Zhy <hyzhou.zhy@alibaba-inc.com> <1187766782@qq.com> Hyzhou Zhy <hyzhou.zhy@alibaba-inc.com> <1187766782@qq.com>
Ilya Khlopotov <ilya.khlopotov@gmail.com> Ilya Khlopotov <ilya.khlopotov@gmail.com>
Iskander Sharipov <quasilyte@gmail.com>
Ivan Markin <sw@nogoegst.net> <twim@riseup.net> Ivan Markin <sw@nogoegst.net> <twim@riseup.net>
Jack Laxson <jackjrabbit@gmail.com> Jack Laxson <jackjrabbit@gmail.com>
Jacob Atzen <jacob@jacobatzen.dk> <jatzen@gmail.com> Jacob Atzen <jacob@jacobatzen.dk> <jatzen@gmail.com>
Jacob Tomlinson <jacob@tom.linson.uk> <jacobtomlinson@users.noreply.github.com> Jacob Tomlinson <jacob@tom.linson.uk> <jacobtomlinson@users.noreply.github.com>
Jaivish Kothari <janonymous.codevulture@gmail.com> Jaivish Kothari <janonymous.codevulture@gmail.com>
Jamie Hannaford <jamie@limetree.org> <jamie.hannaford@rackspace.com> Jamie Hannaford <jamie@limetree.org> <jamie.hannaford@rackspace.com>
Jean Rouge <rougej+github@gmail.com> <jer329@cornell.edu>
Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com> Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com>
Jean-Baptiste Dalido <jeanbaptiste@appgratis.com> Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
Jean-Tiare Le Bigot <jt@yadutaf.fr> <admin@jtlebi.fr> Jean-Tiare Le Bigot <jt@yadutaf.fr> <admin@jtlebi.fr>
Jeff Anderson <jeff@docker.com> <jefferya@programmerq.net> Jeff Anderson <jeff@docker.com> <jefferya@programmerq.net>
Jeff Nickoloff <jeff.nickoloff@gmail.com> <jeff@allingeek.com> Jeff Nickoloff <jeff.nickoloff@gmail.com> <jeff@allingeek.com>
Jeroen Franse <jeroenfranse@gmail.com> Jeroen Franse <jeroenfranse@gmail.com>
Jessica Frazelle <jessfraz@google.com> Jessica Frazelle <acidburn@microsoft.com>
Jessica Frazelle <jessfraz@google.com> <acidburn@docker.com> Jessica Frazelle <acidburn@microsoft.com> <acidburn@docker.com>
Jessica Frazelle <jessfraz@google.com> <acidburn@google.com> Jessica Frazelle <acidburn@microsoft.com> <acidburn@google.com>
Jessica Frazelle <jessfraz@google.com> <jess@docker.com> Jessica Frazelle <acidburn@microsoft.com> <jess@docker.com>
Jessica Frazelle <jessfraz@google.com> <jess@mesosphere.com> Jessica Frazelle <acidburn@microsoft.com> <jess@mesosphere.com>
Jessica Frazelle <jessfraz@google.com> <jfrazelle@users.noreply.github.com> Jessica Frazelle <acidburn@microsoft.com> <jessfraz@google.com>
Jessica Frazelle <jessfraz@google.com> <me@jessfraz.com> Jessica Frazelle <acidburn@microsoft.com> <jfrazelle@users.noreply.github.com>
Jessica Frazelle <jessfraz@google.com> <princess@docker.com> Jessica Frazelle <acidburn@microsoft.com> <me@jessfraz.com>
Jessica Frazelle <acidburn@microsoft.com> <princess@docker.com>
Jian Liao <jliao@alauda.io>
Jiang Jinyang <jjyruby@gmail.com>
Jiang Jinyang <jjyruby@gmail.com> <jiangjinyang@outlook.com>
Jim Galasyn <jim.galasyn@docker.com> Jim Galasyn <jim.galasyn@docker.com>
Jiuyue Ma <majiuyue@huawei.com> Jiuyue Ma <majiuyue@huawei.com>
Joey Geiger <jgeiger@gmail.com> Joey Geiger <jgeiger@gmail.com>
@ -223,7 +238,9 @@ Jon Surrell <jon.surrell@gmail.com> <jon.surrell@automattic.com>
Jordan Arentsen <blissdev@gmail.com> Jordan Arentsen <blissdev@gmail.com>
Jordan Jennings <jjn2009@gmail.com> <jjn2009@users.noreply.github.com> Jordan Jennings <jjn2009@gmail.com> <jjn2009@users.noreply.github.com>
Jorit Kleine-Möllhoff <joppich@bricknet.de> <joppich@users.noreply.github.com> Jorit Kleine-Möllhoff <joppich@bricknet.de> <joppich@users.noreply.github.com>
Jose Diaz-Gonzalez <jose@seatgeek.com> <josegonzalez@users.noreply.github.com> Jose Diaz-Gonzalez <email@josediazgonzalez.com>
Jose Diaz-Gonzalez <email@josediazgonzalez.com> <jose@seatgeek.com>
Jose Diaz-Gonzalez <email@josediazgonzalez.com> <josegonzalez@users.noreply.github.com>
Josh Bonczkowski <josh.bonczkowski@gmail.com> Josh Bonczkowski <josh.bonczkowski@gmail.com>
Josh Eveleth <joshe@opendns.com> <jeveleth@users.noreply.github.com> Josh Eveleth <joshe@opendns.com> <jeveleth@users.noreply.github.com>
Josh Hawn <josh.hawn@docker.com> <jlhawn@berkeley.edu> Josh Hawn <josh.hawn@docker.com> <jlhawn@berkeley.edu>
@ -237,6 +254,7 @@ Justin Cormack <justin.cormack@docker.com>
Justin Cormack <justin.cormack@docker.com> <justin.cormack@unikernel.com> Justin Cormack <justin.cormack@docker.com> <justin.cormack@unikernel.com>
Justin Cormack <justin.cormack@docker.com> <justin@specialbusservice.com> Justin Cormack <justin.cormack@docker.com> <justin@specialbusservice.com>
Justin Simonelis <justin.p.simonelis@gmail.com> <justin.simonelis@PTS-JSIMON2.toronto.exclamation.com> Justin Simonelis <justin.p.simonelis@gmail.com> <justin.simonelis@PTS-JSIMON2.toronto.exclamation.com>
Justin Terry <juterry@microsoft.com>
Jérôme Petazzoni <jerome.petazzoni@docker.com> <jerome.petazzoni@dotcloud.com> Jérôme Petazzoni <jerome.petazzoni@docker.com> <jerome.petazzoni@dotcloud.com>
Jérôme Petazzoni <jerome.petazzoni@docker.com> <jerome.petazzoni@gmail.com> Jérôme Petazzoni <jerome.petazzoni@docker.com> <jerome.petazzoni@gmail.com>
Jérôme Petazzoni <jerome.petazzoni@docker.com> <jp@enix.org> Jérôme Petazzoni <jerome.petazzoni@docker.com> <jp@enix.org>
@ -245,8 +263,11 @@ Kai Qiang Wu (Kennan) <wkq5325@gmail.com>
Kai Qiang Wu (Kennan) <wkq5325@gmail.com> <wkqwu@cn.ibm.com> Kai Qiang Wu (Kennan) <wkq5325@gmail.com> <wkqwu@cn.ibm.com>
Kamil Domański <kamil@domanski.co> Kamil Domański <kamil@domanski.co>
Kamjar Gerami <kami.gerami@gmail.com> Kamjar Gerami <kami.gerami@gmail.com>
Karthik Nayak <karthik.188@gmail.com>
Karthik Nayak <karthik.188@gmail.com> <Karthik.188@gmail.com>
Ken Cochrane <kencochrane@gmail.com> <KenCochrane@gmail.com> Ken Cochrane <kencochrane@gmail.com> <KenCochrane@gmail.com>
Ken Herner <kherner@progress.com> <chosenken@gmail.com> Ken Herner <kherner@progress.com> <chosenken@gmail.com>
Ken Reese <krrgithub@gmail.com>
Kenfe-Mickaël Laventure <mickael.laventure@gmail.com> Kenfe-Mickaël Laventure <mickael.laventure@gmail.com>
Kevin Feyrer <kevin.feyrer@btinternet.com> <kevinfeyrer@users.noreply.github.com> Kevin Feyrer <kevin.feyrer@btinternet.com> <kevinfeyrer@users.noreply.github.com>
Kevin Kern <kaiwentan@harmonycloud.cn> Kevin Kern <kaiwentan@harmonycloud.cn>
@ -260,6 +281,7 @@ Konstantin Pelykh <kpelykh@zettaset.com>
Kotaro Yoshimatsu <kotaro.yoshimatsu@gmail.com> Kotaro Yoshimatsu <kotaro.yoshimatsu@gmail.com>
Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp> <kunal.kushwaha@gmail.com> Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp> <kunal.kushwaha@gmail.com>
Lajos Papp <lajos.papp@sequenceiq.com> <lalyos@yahoo.com> Lajos Papp <lajos.papp@sequenceiq.com> <lalyos@yahoo.com>
Lei Gong <lgong@alauda.io>
Lei Jitang <leijitang@huawei.com> Lei Jitang <leijitang@huawei.com>
Lei Jitang <leijitang@huawei.com> <leijitang@gmail.com> Lei Jitang <leijitang@huawei.com> <leijitang@gmail.com>
Liang Mingqiang <mqliang.zju@gmail.com> Liang Mingqiang <mqliang.zju@gmail.com>
@ -268,7 +290,8 @@ Liao Qingwei <liaoqingwei@huawei.com>
Linus Heckemann <lheckemann@twig-world.com> Linus Heckemann <lheckemann@twig-world.com>
Linus Heckemann <lheckemann@twig-world.com> <anonymouse2048@gmail.com> Linus Heckemann <lheckemann@twig-world.com> <anonymouse2048@gmail.com>
Lokesh Mandvekar <lsm5@fedoraproject.org> <lsm5@redhat.com> Lokesh Mandvekar <lsm5@fedoraproject.org> <lsm5@redhat.com>
Lorenzo Fontana <lo@linux.com> <fontanalorenzo@me.com> Lorenzo Fontana <fontanalorenz@gmail.com> <fontanalorenzo@me.com>
Lorenzo Fontana <fontanalorenz@gmail.com> <lo@linux.com>
Louis Opter <kalessin@kalessin.fr> Louis Opter <kalessin@kalessin.fr>
Louis Opter <kalessin@kalessin.fr> <louis@dotcloud.com> Louis Opter <kalessin@kalessin.fr> <louis@dotcloud.com>
Luca Favatella <luca.favatella@erlang-solutions.com> <lucafavatella@users.noreply.github.com> Luca Favatella <luca.favatella@erlang-solutions.com> <lucafavatella@users.noreply.github.com>
@ -315,6 +338,8 @@ Michael Nussbaum <michael.nussbaum@getbraintree.com>
Michael Nussbaum <michael.nussbaum@getbraintree.com> <code@getbraintree.com> Michael Nussbaum <michael.nussbaum@getbraintree.com> <code@getbraintree.com>
Michael Spetsiotis <michael_spets@hotmail.com> Michael Spetsiotis <michael_spets@hotmail.com>
Michal Minář <miminar@redhat.com> Michal Minář <miminar@redhat.com>
Michiel de Jong <michiel@unhosted.org>
Mickaël Fortunato <morsi.morsicus@gmail.com>
Miguel Angel Alvarez Cabrerizo <doncicuto@gmail.com> <30386061+doncicuto@users.noreply.github.com> Miguel Angel Alvarez Cabrerizo <doncicuto@gmail.com> <30386061+doncicuto@users.noreply.github.com>
Miguel Angel Fernández <elmendalerenda@gmail.com> Miguel Angel Fernández <elmendalerenda@gmail.com>
Mihai Borobocea <MihaiBorob@gmail.com> <MihaiBorobocea@gmail.com> Mihai Borobocea <MihaiBorob@gmail.com> <MihaiBorobocea@gmail.com>
@ -327,6 +352,7 @@ Moorthy RS <rsmoorthy@gmail.com> <rsmoorthy@users.noreply.github.com>
Moysés Borges <moysesb@gmail.com> Moysés Borges <moysesb@gmail.com>
Moysés Borges <moysesb@gmail.com> <moyses.furtado@wplex.com.br> Moysés Borges <moysesb@gmail.com> <moyses.furtado@wplex.com.br>
Nace Oroz <orkica@gmail.com> Nace Oroz <orkica@gmail.com>
Natasha Jarus <linuxmercedes@gmail.com>
Nathan LeClaire <nathan.leclaire@docker.com> <nathan.leclaire@gmail.com> Nathan LeClaire <nathan.leclaire@docker.com> <nathan.leclaire@gmail.com>
Nathan LeClaire <nathan.leclaire@docker.com> <nathanleclaire@gmail.com> Nathan LeClaire <nathan.leclaire@docker.com> <nathanleclaire@gmail.com>
Neil Horman <nhorman@tuxdriver.com> <nhorman@hmswarspite.think-freely.org> Neil Horman <nhorman@tuxdriver.com> <nhorman@hmswarspite.think-freely.org>
@ -338,6 +364,9 @@ Nolan Darilek <nolan@thewordnerd.info>
O.S. Tezer <ostezer@gmail.com> O.S. Tezer <ostezer@gmail.com>
O.S. Tezer <ostezer@gmail.com> <ostezer@users.noreply.github.com> O.S. Tezer <ostezer@gmail.com> <ostezer@users.noreply.github.com>
Oh Jinkyun <tintypemolly@gmail.com> <tintypemolly@Ohui-MacBook-Pro.local> Oh Jinkyun <tintypemolly@gmail.com> <tintypemolly@Ohui-MacBook-Pro.local>
Oliver Reason <oli@overrateddev.co>
Olli Janatuinen <olli.janatuinen@gmail.com>
Olli Janatuinen <olli.janatuinen@gmail.com> <olljanat@users.noreply.github.com>
Ouyang Liduo <oyld0210@163.com> Ouyang Liduo <oyld0210@163.com>
Patrick Stapleton <github@gdi2290.com> Patrick Stapleton <github@gdi2290.com>
Paul Liljenberg <liljenberg.paul@gmail.com> <letters@paulnotcom.se> Paul Liljenberg <liljenberg.paul@gmail.com> <letters@paulnotcom.se>
@ -359,7 +388,10 @@ Robert Terhaar <rterhaar@atlanticdynamic.com> <robbyt@users.noreply.github.com>
Roberto G. Hashioka <roberto.hashioka@docker.com> <roberto_hashioka@hotmail.com> Roberto G. Hashioka <roberto.hashioka@docker.com> <roberto_hashioka@hotmail.com>
Roberto Muñoz Fernández <robertomf@gmail.com> <roberto.munoz.fernandez.contractor@bbva.com> Roberto Muñoz Fernández <robertomf@gmail.com> <roberto.munoz.fernandez.contractor@bbva.com>
Roman Dudin <katrmr@gmail.com> <decadent@users.noreply.github.com> Roman Dudin <katrmr@gmail.com> <decadent@users.noreply.github.com>
Rong Zhang <rongzhang@alauda.io>
Rongxiang Song <tinysong1226@gmail.com>
Ross Boucher <rboucher@gmail.com> Ross Boucher <rboucher@gmail.com>
Rui Cao <ruicao@alauda.io>
Runshen Zhu <runshen.zhu@gmail.com> Runshen Zhu <runshen.zhu@gmail.com>
Ryan Stelly <ryan.stelly@live.com> Ryan Stelly <ryan.stelly@live.com>
Sakeven Jiang <jc5930@sina.cn> Sakeven Jiang <jc5930@sina.cn>
@ -432,6 +464,7 @@ Tõnis Tiigi <tonistiigi@gmail.com>
Trishna Guha <trishnaguha17@gmail.com> Trishna Guha <trishnaguha17@gmail.com>
Tristan Carel <tristan@cogniteev.com> Tristan Carel <tristan@cogniteev.com>
Tristan Carel <tristan@cogniteev.com> <tristan.carel@gmail.com> Tristan Carel <tristan@cogniteev.com> <tristan.carel@gmail.com>
Tyler Brown <tylers.pile@gmail.com>
Umesh Yadav <umesh4257@gmail.com> Umesh Yadav <umesh4257@gmail.com>
Umesh Yadav <umesh4257@gmail.com> <dungeonmaster18@users.noreply.github.com> Umesh Yadav <umesh4257@gmail.com> <dungeonmaster18@users.noreply.github.com>
Victor Lyuboslavsky <victor@victoreda.com> Victor Lyuboslavsky <victor@victoreda.com>
@ -464,8 +497,11 @@ Wei Wu <wuwei4455@gmail.com> cizixs <cizixs@163.com>
Wenjun Tang <tangwj2@lenovo.com> <dodia@163.com> Wenjun Tang <tangwj2@lenovo.com> <dodia@163.com>
Wewang Xiaorenfine <wang.xiaoren@zte.com.cn> Wewang Xiaorenfine <wang.xiaoren@zte.com.cn>
Will Weaver <monkey@buildingbananas.com> Will Weaver <monkey@buildingbananas.com>
Xian Chaobo <xianchaobo@huawei.com>
Xian Chaobo <xianchaobo@huawei.com> <jimmyxian2004@yahoo.com.cn>
Xianglin Gao <xlgao@zju.edu.cn> Xianglin Gao <xlgao@zju.edu.cn>
Xianlu Bird <xianlubird@gmail.com> Xianlu Bird <xianlubird@gmail.com>
Xiaodong Zhang <a4012017@sina.com>
Xiaoyu Zhang <zhang.xiaoyu33@zte.com.cn> Xiaoyu Zhang <zhang.xiaoyu33@zte.com.cn>
Xuecong Liao <satorulogic@gmail.com> Xuecong Liao <satorulogic@gmail.com>
Yamasaki Masahide <masahide.y@gmail.com> Yamasaki Masahide <masahide.y@gmail.com>
@ -477,15 +513,18 @@ Yi EungJun <eungjun.yi@navercorp.com> <semtlenori@gmail.com>
Ying Li <ying.li@docker.com> Ying Li <ying.li@docker.com>
Ying Li <ying.li@docker.com> <cyli@twistedmatrix.com> Ying Li <ying.li@docker.com> <cyli@twistedmatrix.com>
Yong Tang <yong.tang.github@outlook.com> <yongtang@users.noreply.github.com> Yong Tang <yong.tang.github@outlook.com> <yongtang@users.noreply.github.com>
Yongxin Li <yxli@alauda.io>
Yosef Fertel <yfertel@gmail.com> <frosforever@users.noreply.github.com> Yosef Fertel <yfertel@gmail.com> <frosforever@users.noreply.github.com>
Yu Changchun <yuchangchun1@huawei.com> Yu Changchun <yuchangchun1@huawei.com>
Yu Chengxia <yuchengxia@huawei.com> Yu Chengxia <yuchengxia@huawei.com>
Yu Peng <yu.peng36@zte.com.cn> Yu Peng <yu.peng36@zte.com.cn>
Yu Peng <yu.peng36@zte.com.cn> <yupeng36@zte.com.cn> Yu Peng <yu.peng36@zte.com.cn> <yupeng36@zte.com.cn>
Yue Zhang <zy675793960@yeah.net>
Zachary Jaffee <zjaffee@us.ibm.com> <zij@case.edu> Zachary Jaffee <zjaffee@us.ibm.com> <zij@case.edu>
Zachary Jaffee <zjaffee@us.ibm.com> <zjaffee@apache.org> Zachary Jaffee <zjaffee@us.ibm.com> <zjaffee@apache.org>
ZhangHang <stevezhang2014@gmail.com> ZhangHang <stevezhang2014@gmail.com>
Zhenkun Bi <bi.zhenkun@zte.com.cn> Zhenkun Bi <bi.zhenkun@zte.com.cn>
Zhoulin Xie <zhoulin.xie@daocloud.io>
Zhou Hao <zhouhao@cn.fujitsu.com> Zhou Hao <zhouhao@cn.fujitsu.com>
Zhu Kunjia <zhu.kunjia@zte.com.cn> Zhu Kunjia <zhu.kunjia@zte.com.cn>
Zou Yu <zouyu7@huawei.com> Zou Yu <zouyu7@huawei.com>

View File

@ -118,6 +118,7 @@ Andreas Köhler <andi5.py@gmx.net>
Andreas Savvides <andreas@editd.com> Andreas Savvides <andreas@editd.com>
Andreas Tiefenthaler <at@an-ti.eu> Andreas Tiefenthaler <at@an-ti.eu>
Andrei Gherzan <andrei@resin.io> Andrei Gherzan <andrei@resin.io>
Andrei Vagin <avagin@gmail.com>
Andrew C. Bodine <acbodine@us.ibm.com> Andrew C. Bodine <acbodine@us.ibm.com>
Andrew Clay Shafer <andrewcshafer@gmail.com> Andrew Clay Shafer <andrewcshafer@gmail.com>
Andrew Duckworth <grillopress@gmail.com> Andrew Duckworth <grillopress@gmail.com>
@ -137,6 +138,7 @@ Andrew Po <absourd.noise@gmail.com>
Andrew Weiss <andrew.weiss@docker.com> Andrew Weiss <andrew.weiss@docker.com>
Andrew Williams <williams.andrew@gmail.com> Andrew Williams <williams.andrew@gmail.com>
Andrews Medina <andrewsmedina@gmail.com> Andrews Medina <andrewsmedina@gmail.com>
Andrey Kolomentsev <andrey.kolomentsev@docker.com>
Andrey Petrov <andrey.petrov@shazow.net> Andrey Petrov <andrey.petrov@shazow.net>
Andrey Stolbovsky <andrey.stolbovsky@gmail.com> Andrey Stolbovsky <andrey.stolbovsky@gmail.com>
André Martins <aanm90@gmail.com> André Martins <aanm90@gmail.com>
@ -195,23 +197,27 @@ bdevloed <boris.de.vloed@gmail.com>
Ben Bonnefoy <frenchben@docker.com> Ben Bonnefoy <frenchben@docker.com>
Ben Firshman <ben@firshman.co.uk> Ben Firshman <ben@firshman.co.uk>
Ben Golub <ben.golub@dotcloud.com> Ben Golub <ben.golub@dotcloud.com>
Ben Gould <ben@bengould.co.uk>
Ben Hall <ben@benhall.me.uk> Ben Hall <ben@benhall.me.uk>
Ben Sargent <ben@brokendigits.com> Ben Sargent <ben@brokendigits.com>
Ben Severson <BenSeverson@users.noreply.github.com> Ben Severson <BenSeverson@users.noreply.github.com>
Ben Toews <mastahyeti@gmail.com> Ben Toews <mastahyeti@gmail.com>
Ben Wiklund <ben@daisyowl.com> Ben Wiklund <ben@daisyowl.com>
Benjamin Atkin <ben@benatkin.com> Benjamin Atkin <ben@benatkin.com>
Benjamin Baker <Benjamin.baker@utexas.edu>
Benjamin Boudreau <boudreau.benjamin@gmail.com> Benjamin Boudreau <boudreau.benjamin@gmail.com>
Benjamin Yolken <yolken@stripe.com> Benjamin Yolken <yolken@stripe.com>
Benoit Chesneau <bchesneau@gmail.com> Benoit Chesneau <bchesneau@gmail.com>
Bernerd Schaefer <bj.schaefer@gmail.com> Bernerd Schaefer <bj.schaefer@gmail.com>
Bernhard M. Wiedemann <bwiedemann@suse.de> Bernhard M. Wiedemann <bwiedemann@suse.de>
Bert Goethals <bert@bertg.be> Bert Goethals <bert@bertg.be>
Bevisy Zhang <binbin36520@gmail.com>
Bharath Thiruveedula <bharath_ves@hotmail.com> Bharath Thiruveedula <bharath_ves@hotmail.com>
Bhiraj Butala <abhiraj.butala@gmail.com> Bhiraj Butala <abhiraj.butala@gmail.com>
Bhumika Bayani <bhumikabayani@gmail.com> Bhumika Bayani <bhumikabayani@gmail.com>
Bilal Amarni <bilal.amarni@gmail.com> Bilal Amarni <bilal.amarni@gmail.com>
Bill Wang <ozbillwang@gmail.com> Bill Wang <ozbillwang@gmail.com>
Bily Zhang <xcoder@tenxcloud.com>
Bin Liu <liubin0329@gmail.com> Bin Liu <liubin0329@gmail.com>
Bingshen Wang <bingshen.wbs@alibaba-inc.com> Bingshen Wang <bingshen.wbs@alibaba-inc.com>
Blake Geno <blakegeno@gmail.com> Blake Geno <blakegeno@gmail.com>
@ -246,6 +252,7 @@ Brian Torres-Gil <brian@dralth.com>
Brian Trump <btrump@yelp.com> Brian Trump <btrump@yelp.com>
Brice Jaglin <bjaglin@teads.tv> Brice Jaglin <bjaglin@teads.tv>
Briehan Lombaard <briehan.lombaard@gmail.com> Briehan Lombaard <briehan.lombaard@gmail.com>
Brielle Broder <bbroder@google.com>
Bruno Bigras <bigras.bruno@gmail.com> Bruno Bigras <bigras.bruno@gmail.com>
Bruno Binet <bruno.binet@gmail.com> Bruno Binet <bruno.binet@gmail.com>
Bruno Gazzera <bgazzera@paginar.com> Bruno Gazzera <bgazzera@paginar.com>
@ -300,6 +307,7 @@ Chen Min <chenmin46@huawei.com>
Chen Mingjie <chenmingjie0828@163.com> Chen Mingjie <chenmingjie0828@163.com>
Chen Qiu <cheney-90@hotmail.com> Chen Qiu <cheney-90@hotmail.com>
Cheng-mean Liu <soccerl@microsoft.com> Cheng-mean Liu <soccerl@microsoft.com>
Chengfei Shang <cfshang@alauda.io>
Chengguang Xu <cgxu519@gmx.com> Chengguang Xu <cgxu519@gmx.com>
chenyuzhu <chenyuzhi@oschina.cn> chenyuzhu <chenyuzhi@oschina.cn>
Chetan Birajdar <birajdar.chetan@gmail.com> Chetan Birajdar <birajdar.chetan@gmail.com>
@ -325,9 +333,11 @@ Chris Swan <chris.swan@iee.org>
Chris Telfer <ctelfer@docker.com> Chris Telfer <ctelfer@docker.com>
Chris Wahl <github@wahlnetwork.com> Chris Wahl <github@wahlnetwork.com>
Chris Weyl <cweyl@alumni.drew.edu> Chris Weyl <cweyl@alumni.drew.edu>
Chris White <me@cwprogram.com>
Christian Berendt <berendt@b1-systems.de> Christian Berendt <berendt@b1-systems.de>
Christian Brauner <christian.brauner@ubuntu.com> Christian Brauner <christian.brauner@ubuntu.com>
Christian Böhme <developement@boehme3d.de> Christian Böhme <developement@boehme3d.de>
Christian Muehlhaeuser <muesli@gmail.com>
Christian Persson <saser@live.se> Christian Persson <saser@live.se>
Christian Rotzoll <ch.rotzoll@gmail.com> Christian Rotzoll <ch.rotzoll@gmail.com>
Christian Simon <simon@swine.de> Christian Simon <simon@swine.de>
@ -350,6 +360,7 @@ Cody Roseborough <crrosebo@amazon.com>
Coenraad Loubser <coenraad@wish.org.za> Coenraad Loubser <coenraad@wish.org.za>
Colin Dunklau <colin.dunklau@gmail.com> Colin Dunklau <colin.dunklau@gmail.com>
Colin Hebert <hebert.colin@gmail.com> Colin Hebert <hebert.colin@gmail.com>
Colin Panisset <github@clabber.com>
Colin Rice <colin@daedrum.net> Colin Rice <colin@daedrum.net>
Colin Walters <walters@verbum.org> Colin Walters <walters@verbum.org>
Collin Guarino <collin.guarino@gmail.com> Collin Guarino <collin.guarino@gmail.com>
@ -385,6 +396,7 @@ Dan Levy <dan@danlevy.net>
Dan McPherson <dmcphers@redhat.com> Dan McPherson <dmcphers@redhat.com>
Dan Stine <sw@stinemail.com> Dan Stine <sw@stinemail.com>
Dan Williams <me@deedubs.com> Dan Williams <me@deedubs.com>
Dani Hodovic <dani.hodovic@gmail.com>
Dani Louca <dani.louca@docker.com> Dani Louca <dani.louca@docker.com>
Daniel Antlinger <d.antlinger@gmx.at> Daniel Antlinger <d.antlinger@gmx.at>
Daniel Dao <dqminh@cloudflare.com> Daniel Dao <dqminh@cloudflare.com>
@ -438,12 +450,14 @@ David Mackey <tdmackey@booleanhaiku.com>
David Mat <david@davidmat.com> David Mat <david@davidmat.com>
David Mcanulty <github@hellspark.com> David Mcanulty <github@hellspark.com>
David McKay <david@rawkode.com> David McKay <david@rawkode.com>
David P Hilton <david.hilton.p@gmail.com>
David Pelaez <pelaez89@gmail.com> David Pelaez <pelaez89@gmail.com>
David R. Jenni <david.r.jenni@gmail.com> David R. Jenni <david.r.jenni@gmail.com>
David Röthlisberger <david@rothlis.net> David Röthlisberger <david@rothlis.net>
David Sheets <dsheets@docker.com> David Sheets <dsheets@docker.com>
David Sissitka <me@dsissitka.com> David Sissitka <me@dsissitka.com>
David Trott <github@davidtrott.com> David Trott <github@davidtrott.com>
David Wang <00107082@163.com>
David Williamson <david.williamson@docker.com> David Williamson <david.williamson@docker.com>
David Xia <dxia@spotify.com> David Xia <dxia@spotify.com>
David Young <yangboh@cn.ibm.com> David Young <yangboh@cn.ibm.com>
@ -451,8 +465,10 @@ Davide Ceretti <davide.ceretti@hogarthww.com>
Dawn Chen <dawnchen@google.com> Dawn Chen <dawnchen@google.com>
dbdd <wangtong2712@gmail.com> dbdd <wangtong2712@gmail.com>
dcylabs <dcylabs@gmail.com> dcylabs <dcylabs@gmail.com>
Debayan De <debayande@users.noreply.github.com>
Deborah Gertrude Digges <deborah.gertrude.digges@gmail.com> Deborah Gertrude Digges <deborah.gertrude.digges@gmail.com>
deed02392 <georgehafiz@gmail.com> deed02392 <georgehafiz@gmail.com>
Deep Debroy <ddebroy@docker.com>
Deng Guangxing <dengguangxing@huawei.com> Deng Guangxing <dengguangxing@huawei.com>
Deni Bertovic <deni@kset.org> Deni Bertovic <deni@kset.org>
Denis Defreyne <denis@soundcloud.com> Denis Defreyne <denis@soundcloud.com>
@ -477,6 +493,7 @@ Dieter Reuter <dieter.reuter@me.com>
Dillon Dixon <dillondixon@gmail.com> Dillon Dixon <dillondixon@gmail.com>
Dima Stopel <dima@twistlock.com> Dima Stopel <dima@twistlock.com>
Dimitri John Ledkov <dimitri.j.ledkov@intel.com> Dimitri John Ledkov <dimitri.j.ledkov@intel.com>
Dimitris Mandalidis <dimitris.mandalidis@gmail.com>
Dimitris Rozakis <dimrozakis@gmail.com> Dimitris Rozakis <dimrozakis@gmail.com>
Dimitry Andric <d.andric@activevideo.com> Dimitry Andric <d.andric@activevideo.com>
Dinesh Subhraveti <dineshs@altiscale.com> Dinesh Subhraveti <dineshs@altiscale.com>
@ -503,6 +520,7 @@ Don Kjer <don.kjer@gmail.com>
Don Spaulding <donspauldingii@gmail.com> Don Spaulding <donspauldingii@gmail.com>
Donald Huang <don.hcd@gmail.com> Donald Huang <don.hcd@gmail.com>
Dong Chen <dongluo.chen@docker.com> Dong Chen <dongluo.chen@docker.com>
Donghwa Kim <shanytt@gmail.com>
Donovan Jones <git@gamma.net.nz> Donovan Jones <git@gamma.net.nz>
Doron Podoleanu <doronp@il.ibm.com> Doron Podoleanu <doronp@il.ibm.com>
Doug Davis <dug@us.ibm.com> Doug Davis <dug@us.ibm.com>
@ -579,7 +597,9 @@ Ewa Czechowska <ewa@ai-traders.com>
Eystein Måløy Stenberg <eystein.maloy.stenberg@cfengine.com> Eystein Måløy Stenberg <eystein.maloy.stenberg@cfengine.com>
ezbercih <cem.ezberci@gmail.com> ezbercih <cem.ezberci@gmail.com>
Ezra Silvera <ezra@il.ibm.com> Ezra Silvera <ezra@il.ibm.com>
Fabian Kramm <kramm@covexo.com>
Fabian Lauer <kontakt@softwareschmiede-saar.de> Fabian Lauer <kontakt@softwareschmiede-saar.de>
Fabian Raetz <fabian.raetz@gmail.com>
Fabiano Rosas <farosas@br.ibm.com> Fabiano Rosas <farosas@br.ibm.com>
Fabio Falci <fabiofalci@gmail.com> Fabio Falci <fabiofalci@gmail.com>
Fabio Kung <fabio.kung@gmail.com> Fabio Kung <fabio.kung@gmail.com>
@ -591,6 +611,7 @@ Faiz Khan <faizkhan00@gmail.com>
falmp <chico.lopes@gmail.com> falmp <chico.lopes@gmail.com>
Fangming Fang <fangming.fang@arm.com> Fangming Fang <fangming.fang@arm.com>
Fangyuan Gao <21551127@zju.edu.cn> Fangyuan Gao <21551127@zju.edu.cn>
fanjiyun <fan.jiyun@zte.com.cn>
Fareed Dudhia <fareeddudhia@googlemail.com> Fareed Dudhia <fareeddudhia@googlemail.com>
Fathi Boudra <fathi.boudra@linaro.org> Fathi Boudra <fathi.boudra@linaro.org>
Federico Gimenez <fgimenez@coit.es> Federico Gimenez <fgimenez@coit.es>
@ -621,6 +642,7 @@ Florin Patan <florinpatan@gmail.com>
fonglh <fonglh@gmail.com> fonglh <fonglh@gmail.com>
Foysal Iqbal <foysal.iqbal.fb@gmail.com> Foysal Iqbal <foysal.iqbal.fb@gmail.com>
Francesc Campoy <campoy@google.com> Francesc Campoy <campoy@google.com>
Francesco Mari <mari.francesco@gmail.com>
Francis Chuang <francis.chuang@boostport.com> Francis Chuang <francis.chuang@boostport.com>
Francisco Carriedo <fcarriedo@gmail.com> Francisco Carriedo <fcarriedo@gmail.com>
Francisco Souza <f@souza.cc> Francisco Souza <f@souza.cc>
@ -653,6 +675,7 @@ Gaël PORTAY <gael.portay@savoirfairelinux.com>
Genki Takiuchi <genki@s21g.com> Genki Takiuchi <genki@s21g.com>
GennadySpb <lipenkov@gmail.com> GennadySpb <lipenkov@gmail.com>
Geoffrey Bachelet <grosfrais@gmail.com> Geoffrey Bachelet <grosfrais@gmail.com>
Geon Kim <geon0250@gmail.com>
George Kontridze <george@bugsnag.com> George Kontridze <george@bugsnag.com>
George MacRorie <gmacr31@gmail.com> George MacRorie <gmacr31@gmail.com>
George Xie <georgexsh@gmail.com> George Xie <georgexsh@gmail.com>
@ -676,6 +699,7 @@ Gopikannan Venugopalsamy <gopikannan.venugopalsamy@gmail.com>
Gosuke Miyashita <gosukenator@gmail.com> Gosuke Miyashita <gosukenator@gmail.com>
Gou Rao <gou@portworx.com> Gou Rao <gou@portworx.com>
Govinda Fichtner <govinda.fichtner@googlemail.com> Govinda Fichtner <govinda.fichtner@googlemail.com>
Grant Millar <grant@cylo.io>
Grant Reaber <grant.reaber@gmail.com> Grant Reaber <grant.reaber@gmail.com>
Graydon Hoare <graydon@pobox.com> Graydon Hoare <graydon@pobox.com>
Greg Fausak <greg@tacodata.com> Greg Fausak <greg@tacodata.com>
@ -694,7 +718,9 @@ Guruprasad <lgp171188@gmail.com>
Gustav Sinder <gustav.sinder@gmail.com> Gustav Sinder <gustav.sinder@gmail.com>
gwx296173 <gaojing3@huawei.com> gwx296173 <gaojing3@huawei.com>
Günter Zöchbauer <guenter@gzoechbauer.com> Günter Zöchbauer <guenter@gzoechbauer.com>
haikuoliu <haikuo@amazon.com>
Hakan Özler <hakan.ozler@kodcu.com> Hakan Özler <hakan.ozler@kodcu.com>
Hamish Hutchings <moredhel@aoeu.me>
Hans Kristian Flaatten <hans@starefossen.com> Hans Kristian Flaatten <hans@starefossen.com>
Hans Rødtang <hansrodtang@gmail.com> Hans Rødtang <hansrodtang@gmail.com>
Hao Shu Wei <haosw@cn.ibm.com> Hao Shu Wei <haosw@cn.ibm.com>
@ -702,6 +728,7 @@ Hao Zhang <21521210@zju.edu.cn>
Harald Albers <github@albersweb.de> Harald Albers <github@albersweb.de>
Harley Laue <losinggeneration@gmail.com> Harley Laue <losinggeneration@gmail.com>
Harold Cooper <hrldcpr@gmail.com> Harold Cooper <hrldcpr@gmail.com>
Harrison Turton <harrisonturton@gmail.com>
Harry Zhang <harryz@hyper.sh> Harry Zhang <harryz@hyper.sh>
Harshal Patil <harshal.patil@in.ibm.com> Harshal Patil <harshal.patil@in.ibm.com>
Harshal Patil <harshalp@linux.vnet.ibm.com> Harshal Patil <harshalp@linux.vnet.ibm.com>
@ -713,6 +740,7 @@ Hector Castro <hectcastro@gmail.com>
Helen Xie <chenjg@harmonycloud.cn> Helen Xie <chenjg@harmonycloud.cn>
Henning Sprang <henning.sprang@gmail.com> Henning Sprang <henning.sprang@gmail.com>
Hiroshi Hatake <hatake@clear-code.com> Hiroshi Hatake <hatake@clear-code.com>
Hiroyuki Sasagawa <hs19870702@gmail.com>
Hobofan <goisser94@gmail.com> Hobofan <goisser94@gmail.com>
Hollie Teal <hollie@docker.com> Hollie Teal <hollie@docker.com>
Hong Xu <hong@topbug.net> Hong Xu <hong@topbug.net>
@ -735,6 +763,7 @@ Ian Bishop <ianbishop@pace7.com>
Ian Bull <irbull@gmail.com> Ian Bull <irbull@gmail.com>
Ian Calvert <ianjcalvert@gmail.com> Ian Calvert <ianjcalvert@gmail.com>
Ian Campbell <ian.campbell@docker.com> Ian Campbell <ian.campbell@docker.com>
Ian Chen <ianre657@gmail.com>
Ian Lee <IanLee1521@gmail.com> Ian Lee <IanLee1521@gmail.com>
Ian Main <imain@redhat.com> Ian Main <imain@redhat.com>
Ian Philpot <ian.philpot@microsoft.com> Ian Philpot <ian.philpot@microsoft.com>
@ -752,9 +781,11 @@ Ilya Khlopotov <ilya.khlopotov@gmail.com>
imre Fitos <imre.fitos+github@gmail.com> imre Fitos <imre.fitos+github@gmail.com>
inglesp <peter.inglesby@gmail.com> inglesp <peter.inglesby@gmail.com>
Ingo Gottwald <in.gottwald@gmail.com> Ingo Gottwald <in.gottwald@gmail.com>
Innovimax <innovimax@gmail.com>
Isaac Dupree <antispam@idupree.com> Isaac Dupree <antispam@idupree.com>
Isabel Jimenez <contact.isabeljimenez@gmail.com> Isabel Jimenez <contact.isabeljimenez@gmail.com>
Isao Jonas <isao.jonas@gmail.com> Isao Jonas <isao.jonas@gmail.com>
Iskander Sharipov <quasilyte@gmail.com>
Ivan Babrou <ibobrik@gmail.com> Ivan Babrou <ibobrik@gmail.com>
Ivan Fraixedes <ifcdev@gmail.com> Ivan Fraixedes <ifcdev@gmail.com>
Ivan Grcic <igrcic@gmail.com> Ivan Grcic <igrcic@gmail.com>
@ -785,6 +816,7 @@ James Mills <prologic@shortcircuit.net.au>
James Nesbitt <james.nesbitt@wunderkraut.com> James Nesbitt <james.nesbitt@wunderkraut.com>
James Nugent <james@jen20.com> James Nugent <james@jen20.com>
James Turnbull <james@lovedthanlost.net> James Turnbull <james@lovedthanlost.net>
James Watkins-Harvey <jwatkins@progi-media.com>
Jamie Hannaford <jamie@limetree.org> Jamie Hannaford <jamie@limetree.org>
Jamshid Afshar <jafshar@yahoo.com> Jamshid Afshar <jafshar@yahoo.com>
Jan Keromnes <janx@linux.com> Jan Keromnes <janx@linux.com>
@ -817,6 +849,7 @@ jaxgeller <jacksongeller@gmail.com>
Jay <imjching@hotmail.com> Jay <imjching@hotmail.com>
Jay <teguhwpurwanto@gmail.com> Jay <teguhwpurwanto@gmail.com>
Jay Kamat <github@jgkamat.33mail.com> Jay Kamat <github@jgkamat.33mail.com>
Jean Rouge <rougej+github@gmail.com>
Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com> Jean-Baptiste Barth <jeanbaptiste.barth@gmail.com>
Jean-Baptiste Dalido <jeanbaptiste@appgratis.com> Jean-Baptiste Dalido <jeanbaptiste@appgratis.com>
Jean-Christophe Berthon <huygens@berthon.eu> Jean-Christophe Berthon <huygens@berthon.eu>
@ -847,11 +880,13 @@ Jeroen Franse <jeroenfranse@gmail.com>
Jeroen Jacobs <github@jeroenj.be> Jeroen Jacobs <github@jeroenj.be>
Jesse Dearing <jesse.dearing@gmail.com> Jesse Dearing <jesse.dearing@gmail.com>
Jesse Dubay <jesse@thefortytwo.net> Jesse Dubay <jesse@thefortytwo.net>
Jessica Frazelle <jessfraz@google.com> Jessica Frazelle <acidburn@microsoft.com>
Jezeniel Zapanta <jpzapanta22@gmail.com> Jezeniel Zapanta <jpzapanta22@gmail.com>
Jhon Honce <jhonce@redhat.com> Jhon Honce <jhonce@redhat.com>
Ji.Zhilong <zhilongji@gmail.com> Ji.Zhilong <zhilongji@gmail.com>
Jian Liao <jliao@alauda.io>
Jian Zhang <zhangjian.fnst@cn.fujitsu.com> Jian Zhang <zhangjian.fnst@cn.fujitsu.com>
Jiang Jinyang <jjyruby@gmail.com>
Jie Luo <luo612@zju.edu.cn> Jie Luo <luo612@zju.edu.cn>
Jihyun Hwang <jhhwang@telcoware.com> Jihyun Hwang <jhhwang@telcoware.com>
Jilles Oldenbeuving <ojilles@gmail.com> Jilles Oldenbeuving <ojilles@gmail.com>
@ -862,14 +897,13 @@ Jim Perrin <jperrin@centos.org>
Jimmy Cuadra <jimmy@jimmycuadra.com> Jimmy Cuadra <jimmy@jimmycuadra.com>
Jimmy Puckett <jimmy.puckett@spinen.com> Jimmy Puckett <jimmy.puckett@spinen.com>
Jimmy Song <rootsongjc@gmail.com> Jimmy Song <rootsongjc@gmail.com>
jimmyxian <jimmyxian2004@yahoo.com.cn>
Jinsoo Park <cellpjs@gmail.com> Jinsoo Park <cellpjs@gmail.com>
Jiri Appl <jiria@microsoft.com>
Jiri Popelka <jpopelka@redhat.com> Jiri Popelka <jpopelka@redhat.com>
Jiuyue Ma <majiuyue@huawei.com> Jiuyue Ma <majiuyue@huawei.com>
Jiří Župka <jzupka@redhat.com> Jiří Župka <jzupka@redhat.com>
jjy <jiangjinyang@outlook.com>
jmzwcn <jmzwcn@gmail.com>
Joao Fernandes <joao.fernandes@docker.com> Joao Fernandes <joao.fernandes@docker.com>
Joao Trindade <trindade.joao@gmail.com>
Joe Beda <joe.github@bedafamily.com> Joe Beda <joe.github@bedafamily.com>
Joe Doliner <jdoliner@pachyderm.io> Joe Doliner <jdoliner@pachyderm.io>
Joe Ferguson <joe@infosiftr.com> Joe Ferguson <joe@infosiftr.com>
@ -908,6 +942,7 @@ Jon Johnson <jonjohnson@google.com>
Jon Surrell <jon.surrell@gmail.com> Jon Surrell <jon.surrell@gmail.com>
Jon Wedaman <jweede@gmail.com> Jon Wedaman <jweede@gmail.com>
Jonas Pfenniger <jonas@pfenniger.name> Jonas Pfenniger <jonas@pfenniger.name>
Jonathan A. Schweder <jonathanschweder@gmail.com>
Jonathan A. Sternberg <jonathansternberg@gmail.com> Jonathan A. Sternberg <jonathansternberg@gmail.com>
Jonathan Boulle <jonathanboulle@gmail.com> Jonathan Boulle <jonathanboulle@gmail.com>
Jonathan Camp <jonathan@irondojo.com> Jonathan Camp <jonathan@irondojo.com>
@ -928,7 +963,7 @@ Jordan Jennings <jjn2009@gmail.com>
Jordan Sissel <jls@semicomplete.com> Jordan Sissel <jls@semicomplete.com>
Jorge Marin <chipironcin@users.noreply.github.com> Jorge Marin <chipironcin@users.noreply.github.com>
Jorit Kleine-Möllhoff <joppich@bricknet.de> Jorit Kleine-Möllhoff <joppich@bricknet.de>
Jose Diaz-Gonzalez <jose@seatgeek.com> Jose Diaz-Gonzalez <email@josediazgonzalez.com>
Joseph Anthony Pasquale Holsten <joseph@josephholsten.com> Joseph Anthony Pasquale Holsten <joseph@josephholsten.com>
Joseph Hager <ajhager@gmail.com> Joseph Hager <ajhager@gmail.com>
Joseph Kern <jkern@semafour.net> Joseph Kern <jkern@semafour.net>
@ -982,7 +1017,8 @@ kargakis <kargakis@users.noreply.github.com>
Karl Grzeszczak <karlgrz@gmail.com> Karl Grzeszczak <karlgrz@gmail.com>
Karol Duleba <mr.fuxi@gmail.com> Karol Duleba <mr.fuxi@gmail.com>
Karthik Karanth <karanth.karthik@gmail.com> Karthik Karanth <karanth.karthik@gmail.com>
Karthik Nayak <Karthik.188@gmail.com> Karthik Nayak <karthik.188@gmail.com>
Kasper Fabæch Brandt <poizan@poizan.dk>
Kate Heddleston <kate.heddleston@gmail.com> Kate Heddleston <kate.heddleston@gmail.com>
Katie McLaughlin <katie@glasnt.com> Katie McLaughlin <katie@glasnt.com>
Kato Kazuyoshi <kato.kazuyoshi@gmail.com> Kato Kazuyoshi <kato.kazuyoshi@gmail.com>
@ -990,6 +1026,7 @@ Katrina Owen <katrina.owen@gmail.com>
Kawsar Saiyeed <kawsar.saiyeed@projiris.com> Kawsar Saiyeed <kawsar.saiyeed@projiris.com>
Kay Yan <kay.yan@daocloud.io> Kay Yan <kay.yan@daocloud.io>
kayrus <kay.diam@gmail.com> kayrus <kay.diam@gmail.com>
Kazuhiro Sera <seratch@gmail.com>
Ke Li <kel@splunk.com> Ke Li <kel@splunk.com>
Ke Xu <leonhartx.k@gmail.com> Ke Xu <leonhartx.k@gmail.com>
Kei Ohmura <ohmura.kei@gmail.com> Kei Ohmura <ohmura.kei@gmail.com>
@ -998,6 +1035,7 @@ Keli Hu <dev@keli.hu>
Ken Cochrane <kencochrane@gmail.com> Ken Cochrane <kencochrane@gmail.com>
Ken Herner <kherner@progress.com> Ken Herner <kherner@progress.com>
Ken ICHIKAWA <ichikawa.ken@jp.fujitsu.com> Ken ICHIKAWA <ichikawa.ken@jp.fujitsu.com>
Ken Reese <krrgithub@gmail.com>
Kenfe-Mickaël Laventure <mickael.laventure@gmail.com> Kenfe-Mickaël Laventure <mickael.laventure@gmail.com>
Kenjiro Nakayama <nakayamakenjiro@gmail.com> Kenjiro Nakayama <nakayamakenjiro@gmail.com>
Kent Johnson <kentoj@gmail.com> Kent Johnson <kentoj@gmail.com>
@ -1035,9 +1073,10 @@ Krasimir Georgiev <support@vip-consult.co.uk>
Kris-Mikael Krister <krismikael@protonmail.com> Kris-Mikael Krister <krismikael@protonmail.com>
Kristian Haugene <kristian.haugene@capgemini.com> Kristian Haugene <kristian.haugene@capgemini.com>
Kristina Zabunova <triara.xiii@gmail.com> Kristina Zabunova <triara.xiii@gmail.com>
krrg <krrgithub@gmail.com> Krystian Wojcicki <kwojcicki@sympatico.ca>
Kun Zhang <zkazure@gmail.com> Kun Zhang <zkazure@gmail.com>
Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp> Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp>
Kunal Tyagi <tyagi.kunal@live.com>
Kyle Conroy <kyle.j.conroy@gmail.com> Kyle Conroy <kyle.j.conroy@gmail.com>
Kyle Linden <linden.kyle@gmail.com> Kyle Linden <linden.kyle@gmail.com>
kyu <leehk1227@gmail.com> kyu <leehk1227@gmail.com>
@ -1060,6 +1099,7 @@ Leandro Siqueira <leandro.siqueira@gmail.com>
Lee Chao <932819864@qq.com> Lee Chao <932819864@qq.com>
Lee, Meng-Han <sunrisedm4@gmail.com> Lee, Meng-Han <sunrisedm4@gmail.com>
leeplay <hyeongkyu.lee@navercorp.com> leeplay <hyeongkyu.lee@navercorp.com>
Lei Gong <lgong@alauda.io>
Lei Jitang <leijitang@huawei.com> Lei Jitang <leijitang@huawei.com>
Len Weincier <len@cloudafrica.net> Len Weincier <len@cloudafrica.net>
Lennie <github@consolejunkie.net> Lennie <github@consolejunkie.net>
@ -1076,6 +1116,8 @@ Liana Lo <liana.lixia@gmail.com>
Liang Mingqiang <mqliang.zju@gmail.com> Liang Mingqiang <mqliang.zju@gmail.com>
Liang-Chi Hsieh <viirya@gmail.com> Liang-Chi Hsieh <viirya@gmail.com>
Liao Qingwei <liaoqingwei@huawei.com> Liao Qingwei <liaoqingwei@huawei.com>
Lifubang <lifubang@acmcoder.com>
Lihua Tang <lhtang@alauda.io>
Lily Guo <lily.guo@docker.com> Lily Guo <lily.guo@docker.com>
limsy <seongyeol37@gmail.com> limsy <seongyeol37@gmail.com>
Lin Lu <doraalin@163.com> Lin Lu <doraalin@163.com>
@ -1094,7 +1136,8 @@ Lloyd Dewolf <foolswisdom@gmail.com>
Lokesh Mandvekar <lsm5@fedoraproject.org> Lokesh Mandvekar <lsm5@fedoraproject.org>
longliqiang88 <394564827@qq.com> longliqiang88 <394564827@qq.com>
Lorenz Leutgeb <lorenz.leutgeb@gmail.com> Lorenz Leutgeb <lorenz.leutgeb@gmail.com>
Lorenzo Fontana <lo@linux.com> Lorenzo Fontana <fontanalorenz@gmail.com>
Lotus Fenn <fenn.lotus@gmail.com>
Louis Opter <kalessin@kalessin.fr> Louis Opter <kalessin@kalessin.fr>
Luca Favatella <luca.favatella@erlang-solutions.com> Luca Favatella <luca.favatella@erlang-solutions.com>
Luca Marturana <lucamarturana@gmail.com> Luca Marturana <lucamarturana@gmail.com>
@ -1151,6 +1194,7 @@ Marius Gundersen <me@mariusgundersen.net>
Marius Sturm <marius@graylog.com> Marius Sturm <marius@graylog.com>
Marius Voila <marius.voila@gmail.com> Marius Voila <marius.voila@gmail.com>
Mark Allen <mrallen1@yahoo.com> Mark Allen <mrallen1@yahoo.com>
Mark Jeromin <mark.jeromin@sysfrog.net>
Mark McGranaghan <mmcgrana@gmail.com> Mark McGranaghan <mmcgrana@gmail.com>
Mark McKinstry <mmckinst@umich.edu> Mark McKinstry <mmckinst@umich.edu>
Mark Milstein <mark@epiloque.com> Mark Milstein <mark@epiloque.com>
@ -1167,6 +1211,7 @@ Martijn van Oosterhout <kleptog@svana.org>
Martin Honermeyer <maze@strahlungsfrei.de> Martin Honermeyer <maze@strahlungsfrei.de>
Martin Kelly <martin@surround.io> Martin Kelly <martin@surround.io>
Martin Mosegaard Amdisen <martin.amdisen@praqma.com> Martin Mosegaard Amdisen <martin.amdisen@praqma.com>
Martin Muzatko <martin@happy-css.com>
Martin Redmond <redmond.martin@gmail.com> Martin Redmond <redmond.martin@gmail.com>
Mary Anthony <mary.anthony@docker.com> Mary Anthony <mary.anthony@docker.com>
Masahito Zembutsu <zembutsu@users.noreply.github.com> Masahito Zembutsu <zembutsu@users.noreply.github.com>
@ -1200,6 +1245,7 @@ Matthias Klumpp <matthias@tenstral.net>
Matthias Kühnle <git.nivoc@neverbox.com> Matthias Kühnle <git.nivoc@neverbox.com>
Matthias Rampke <mr@soundcloud.com> Matthias Rampke <mr@soundcloud.com>
Matthieu Hauglustaine <matt.hauglustaine@gmail.com> Matthieu Hauglustaine <matt.hauglustaine@gmail.com>
Mattias Jernberg <nostrad@gmail.com>
Mauricio Garavaglia <mauricio@medallia.com> Mauricio Garavaglia <mauricio@medallia.com>
mauriyouth <mauriyouth@gmail.com> mauriyouth <mauriyouth@gmail.com>
Max Shytikov <mshytikov@gmail.com> Max Shytikov <mshytikov@gmail.com>
@ -1208,6 +1254,7 @@ Maxim Ivanov <ivanov.maxim@gmail.com>
Maxim Kulkin <mkulkin@mirantis.com> Maxim Kulkin <mkulkin@mirantis.com>
Maxim Treskin <zerthurd@gmail.com> Maxim Treskin <zerthurd@gmail.com>
Maxime Petazzoni <max@signalfuse.com> Maxime Petazzoni <max@signalfuse.com>
Maximiliano Maccanti <maccanti@amazon.com>
Meaglith Ma <genedna@gmail.com> Meaglith Ma <genedna@gmail.com>
meejah <meejah@meejah.ca> meejah <meejah@meejah.ca>
Megan Kostick <mkostick@us.ibm.com> Megan Kostick <mkostick@us.ibm.com>
@ -1248,8 +1295,9 @@ Michal Wieczorek <wieczorek-michal@wp.pl>
Michaël Pailloncy <mpapo.dev@gmail.com> Michaël Pailloncy <mpapo.dev@gmail.com>
Michał Czeraszkiewicz <czerasz@gmail.com> Michał Czeraszkiewicz <czerasz@gmail.com>
Michał Gryko <github@odkurzacz.org> Michał Gryko <github@odkurzacz.org>
Michiel@unhosted <michiel@unhosted.org> Michiel de Jong <michiel@unhosted.org>
Mickaël FORTUNATO <morsi.morsicus@gmail.com> Mickaël Fortunato <morsi.morsicus@gmail.com>
Mickaël Remars <mickael@remars.com>
Miguel Angel Fernández <elmendalerenda@gmail.com> Miguel Angel Fernández <elmendalerenda@gmail.com>
Miguel Morales <mimoralea@gmail.com> Miguel Morales <mimoralea@gmail.com>
Mihai Borobocea <MihaiBorob@gmail.com> Mihai Borobocea <MihaiBorob@gmail.com>
@ -1280,6 +1328,7 @@ Mitch Capper <mitch.capper@gmail.com>
Mizuki Urushida <z11111001011@gmail.com> Mizuki Urushida <z11111001011@gmail.com>
mlarcher <github@ringabell.org> mlarcher <github@ringabell.org>
Mohammad Banikazemi <mb@us.ibm.com> Mohammad Banikazemi <mb@us.ibm.com>
Mohammad Nasirifar <farnasirim@gmail.com>
Mohammed Aaqib Ansari <maaquib@gmail.com> Mohammed Aaqib Ansari <maaquib@gmail.com>
Mohit Soni <mosoni@ebay.com> Mohit Soni <mosoni@ebay.com>
Moorthy RS <rsmoorthy@gmail.com> Moorthy RS <rsmoorthy@gmail.com>
@ -1304,6 +1353,7 @@ Nan Monnand Deng <monnand@gmail.com>
Naoki Orii <norii@cs.cmu.edu> Naoki Orii <norii@cs.cmu.edu>
Natalie Parker <nparker@omnifone.com> Natalie Parker <nparker@omnifone.com>
Natanael Copa <natanael.copa@docker.com> Natanael Copa <natanael.copa@docker.com>
Natasha Jarus <linuxmercedes@gmail.com>
Nate Brennand <nate.brennand@clever.com> Nate Brennand <nate.brennand@clever.com>
Nate Eagleson <nate@nateeag.com> Nate Eagleson <nate@nateeag.com>
Nate Jones <nate@endot.org> Nate Jones <nate@endot.org>
@ -1337,6 +1387,7 @@ Nicolas Dudebout <nicolas.dudebout@gatech.edu>
Nicolas Goy <kuon@goyman.com> Nicolas Goy <kuon@goyman.com>
Nicolas Kaiser <nikai@nikai.net> Nicolas Kaiser <nikai@nikai.net>
Nicolas Sterchele <sterchele.nicolas@gmail.com> Nicolas Sterchele <sterchele.nicolas@gmail.com>
Nicolas V Castet <nvcastet@us.ibm.com>
Nicolás Hock Isaza <nhocki@gmail.com> Nicolás Hock Isaza <nhocki@gmail.com>
Nigel Poulton <nigelpoulton@hotmail.com> Nigel Poulton <nigelpoulton@hotmail.com>
Nik Nyby <nikolas@gnu.org> Nik Nyby <nikolas@gnu.org>
@ -1352,6 +1403,7 @@ Noah Treuhaft <noah.treuhaft@docker.com>
NobodyOnSE <ich@sektor.selfip.com> NobodyOnSE <ich@sektor.selfip.com>
noducks <onemannoducks@gmail.com> noducks <onemannoducks@gmail.com>
Nolan Darilek <nolan@thewordnerd.info> Nolan Darilek <nolan@thewordnerd.info>
Noriki Nakamura <noriki.nakamura@miraclelinux.com>
nponeccop <andy.melnikov@gmail.com> nponeccop <andy.melnikov@gmail.com>
Nuutti Kotivuori <naked@iki.fi> Nuutti Kotivuori <naked@iki.fi>
nzwsch <hi@nzwsch.com> nzwsch <hi@nzwsch.com>
@ -1363,8 +1415,11 @@ Ohad Schneider <ohadschn@users.noreply.github.com>
ohmystack <jun.jiang02@ele.me> ohmystack <jun.jiang02@ele.me>
Ole Reifschneider <mail@ole-reifschneider.de> Ole Reifschneider <mail@ole-reifschneider.de>
Oliver Neal <ItsVeryWindy@users.noreply.github.com> Oliver Neal <ItsVeryWindy@users.noreply.github.com>
Oliver Reason <oli@overrateddev.co>
Olivier Gambier <dmp42@users.noreply.github.com> Olivier Gambier <dmp42@users.noreply.github.com>
Olle Jonsson <olle.jonsson@gmail.com> Olle Jonsson <olle.jonsson@gmail.com>
Olli Janatuinen <olli.janatuinen@gmail.com>
Omri Shiv <Omri.Shiv@teradata.com>
Oriol Francès <oriolfa@gmail.com> Oriol Francès <oriolfa@gmail.com>
Oskar Niburski <oskarniburski@gmail.com> Oskar Niburski <oskarniburski@gmail.com>
Otto Kekäläinen <otto@seravo.fi> Otto Kekäläinen <otto@seravo.fi>
@ -1420,6 +1475,7 @@ Peter Edge <peter.edge@gmail.com>
Peter Ericson <pdericson@gmail.com> Peter Ericson <pdericson@gmail.com>
Peter Esbensen <pkesbensen@gmail.com> Peter Esbensen <pkesbensen@gmail.com>
Peter Jaffe <pjaffe@nevo.com> Peter Jaffe <pjaffe@nevo.com>
Peter Kang <peter@spell.run>
Peter Malmgren <ptmalmgren@gmail.com> Peter Malmgren <ptmalmgren@gmail.com>
Peter Salvatore <peter@psftw.com> Peter Salvatore <peter@psftw.com>
Peter Volpe <petervo@redhat.com> Peter Volpe <petervo@redhat.com>
@ -1452,6 +1508,7 @@ Prasanna Gautam <prasannagautam@gmail.com>
Pratik Karki <prertik@outlook.com> Pratik Karki <prertik@outlook.com>
Prayag Verma <prayag.verma@gmail.com> Prayag Verma <prayag.verma@gmail.com>
Priya Wadhwa <priyawadhwa@google.com> Priya Wadhwa <priyawadhwa@google.com>
Projjol Banerji <probaner23@gmail.com>
Przemek Hejman <przemyslaw.hejman@gmail.com> Przemek Hejman <przemyslaw.hejman@gmail.com>
Pure White <daniel48@126.com> Pure White <daniel48@126.com>
pysqz <randomq@126.com> pysqz <randomq@126.com>
@ -1475,6 +1532,7 @@ Ralph Bean <rbean@redhat.com>
Ramkumar Ramachandra <artagnon@gmail.com> Ramkumar Ramachandra <artagnon@gmail.com>
Ramon Brooker <rbrooker@aetherealmind.com> Ramon Brooker <rbrooker@aetherealmind.com>
Ramon van Alteren <ramon@vanalteren.nl> Ramon van Alteren <ramon@vanalteren.nl>
RaviTeja Pothana <ravi-teja@live.com>
Ray Tsang <rayt@google.com> Ray Tsang <rayt@google.com>
ReadmeCritic <frankensteinbot@gmail.com> ReadmeCritic <frankensteinbot@gmail.com>
Recursive Madman <recursive.madman@gmx.de> Recursive Madman <recursive.madman@gmx.de>
@ -1524,6 +1582,7 @@ Roel Van Nyen <roel.vannyen@gmail.com>
Roger Peppe <rogpeppe@gmail.com> Roger Peppe <rogpeppe@gmail.com>
Rohit Jnagal <jnagal@google.com> Rohit Jnagal <jnagal@google.com>
Rohit Kadam <rohit.d.kadam@gmail.com> Rohit Kadam <rohit.d.kadam@gmail.com>
Rohit Kapur <rkapur@flatiron.com>
Rojin George <rojingeorge@huawei.com> Rojin George <rojingeorge@huawei.com>
Roland Huß <roland@jolokia.org> Roland Huß <roland@jolokia.org>
Roland Kammerer <roland.kammerer@linbit.com> Roland Kammerer <roland.kammerer@linbit.com>
@ -1533,6 +1592,9 @@ Roman Dudin <katrmr@gmail.com>
Roman Strashkin <roman.strashkin@gmail.com> Roman Strashkin <roman.strashkin@gmail.com>
Ron Smits <ron.smits@gmail.com> Ron Smits <ron.smits@gmail.com>
Ron Williams <ron.a.williams@gmail.com> Ron Williams <ron.a.williams@gmail.com>
Rong Gao <gaoronggood@163.com>
Rong Zhang <rongzhang@alauda.io>
Rongxiang Song <tinysong1226@gmail.com>
root <docker-dummy@example.com> root <docker-dummy@example.com>
root <root@lxdebmas.marist.edu> root <root@lxdebmas.marist.edu>
root <root@ubuntu-14.04-amd64-vbox> root <root@ubuntu-14.04-amd64-vbox>
@ -1544,8 +1606,10 @@ Rovanion Luckey <rovanion.luckey@gmail.com>
Royce Remer <royceremer@gmail.com> Royce Remer <royceremer@gmail.com>
Rozhnov Alexandr <nox73@ya.ru> Rozhnov Alexandr <nox73@ya.ru>
Rudolph Gottesheim <r.gottesheim@loot.at> Rudolph Gottesheim <r.gottesheim@loot.at>
Rui Cao <ruicao@alauda.io>
Rui Lopes <rgl@ruilopes.com> Rui Lopes <rgl@ruilopes.com>
Runshen Zhu <runshen.zhu@gmail.com> Runshen Zhu <runshen.zhu@gmail.com>
Russ Magee <rmagee@gmail.com>
Ryan Abrams <rdabrams@gmail.com> Ryan Abrams <rdabrams@gmail.com>
Ryan Anderson <anderson.ryanc@gmail.com> Ryan Anderson <anderson.ryanc@gmail.com>
Ryan Aslett <github@mixologic.com> Ryan Aslett <github@mixologic.com>
@ -1564,6 +1628,7 @@ Ryan Wallner <ryan.wallner@clusterhq.com>
Ryan Zhang <ryan.zhang@docker.com> Ryan Zhang <ryan.zhang@docker.com>
ryancooper7 <ryan.cooper7@gmail.com> ryancooper7 <ryan.cooper7@gmail.com>
RyanDeng <sheldon.d1018@gmail.com> RyanDeng <sheldon.d1018@gmail.com>
Ryo Nakao <nakabonne@gmail.com>
Rémy Greinhofer <remy.greinhofer@livelovely.com> Rémy Greinhofer <remy.greinhofer@livelovely.com>
s. rannou <mxs@sbrk.org> s. rannou <mxs@sbrk.org>
s00318865 <sunyuan3@huawei.com> s00318865 <sunyuan3@huawei.com>
@ -1572,6 +1637,7 @@ Sachin Joshi <sachin_jayant_joshi@hotmail.com>
Sagar Hani <sagarhani33@gmail.com> Sagar Hani <sagarhani33@gmail.com>
Sainath Grandhi <sainath.grandhi@intel.com> Sainath Grandhi <sainath.grandhi@intel.com>
Sakeven Jiang <jc5930@sina.cn> Sakeven Jiang <jc5930@sina.cn>
Salahuddin Khan <salah@docker.com>
Sally O'Malley <somalley@redhat.com> Sally O'Malley <somalley@redhat.com>
Sam Abed <sam.abed@gmail.com> Sam Abed <sam.abed@gmail.com>
Sam Alba <sam.alba@gmail.com> Sam Alba <sam.alba@gmail.com>
@ -1593,6 +1659,7 @@ Santhosh Manohar <santhosh@docker.com>
sapphiredev <se.imas.kr@gmail.com> sapphiredev <se.imas.kr@gmail.com>
Sargun Dhillon <sargun@netflix.com> Sargun Dhillon <sargun@netflix.com>
Sascha Andres <sascha.andres@outlook.com> Sascha Andres <sascha.andres@outlook.com>
Sascha Grunert <sgrunert@suse.com>
Satnam Singh <satnam@raintown.org> Satnam Singh <satnam@raintown.org>
Satoshi Amemiya <satoshi_amemiya@voyagegroup.com> Satoshi Amemiya <satoshi_amemiya@voyagegroup.com>
Satoshi Tagomori <tagomoris@gmail.com> Satoshi Tagomori <tagomoris@gmail.com>
@ -1619,7 +1686,9 @@ Serge Hallyn <serge.hallyn@ubuntu.com>
Sergey Alekseev <sergey.alekseev.minsk@gmail.com> Sergey Alekseev <sergey.alekseev.minsk@gmail.com>
Sergey Evstifeev <sergey.evstifeev@gmail.com> Sergey Evstifeev <sergey.evstifeev@gmail.com>
Sergii Kabashniuk <skabashnyuk@codenvy.com> Sergii Kabashniuk <skabashnyuk@codenvy.com>
Sergio Lopez <slp@redhat.com>
Serhat Gülçiçek <serhat25@gmail.com> Serhat Gülçiçek <serhat25@gmail.com>
SeungUkLee <lsy931106@gmail.com>
Sevki Hasirci <s@sevki.org> Sevki Hasirci <s@sevki.org>
Shane Canon <scanon@lbl.gov> Shane Canon <scanon@lbl.gov>
Shane da Silva <shane@dasilva.io> Shane da Silva <shane@dasilva.io>
@ -1647,6 +1716,7 @@ Sidhartha Mani <sidharthamn@gmail.com>
sidharthamani <sid@rancher.com> sidharthamani <sid@rancher.com>
Silas Sewell <silas@sewell.org> Silas Sewell <silas@sewell.org>
Silvan Jegen <s.jegen@gmail.com> Silvan Jegen <s.jegen@gmail.com>
Simão Reis <smnrsti@gmail.com>
Simei He <hesimei@zju.edu.cn> Simei He <hesimei@zju.edu.cn>
Simon Eskildsen <sirup@sirupsen.com> Simon Eskildsen <sirup@sirupsen.com>
Simon Ferquel <simon.ferquel@docker.com> Simon Ferquel <simon.ferquel@docker.com>
@ -1714,10 +1784,11 @@ tang0th <tang0th@gmx.com>
Tangi Colin <tangicolin@gmail.com> Tangi Colin <tangicolin@gmail.com>
Tatsuki Sugiura <sugi@nemui.org> Tatsuki Sugiura <sugi@nemui.org>
Tatsushi Inagaki <e29253@jp.ibm.com> Tatsushi Inagaki <e29253@jp.ibm.com>
Taylan Isikdemir <taylani@google.com>
Taylor Jones <monitorjbl@gmail.com> Taylor Jones <monitorjbl@gmail.com>
tbonza <tylers.pile@gmail.com>
Ted M. Young <tedyoung@gmail.com> Ted M. Young <tedyoung@gmail.com>
Tehmasp Chaudhri <tehmasp@gmail.com> Tehmasp Chaudhri <tehmasp@gmail.com>
Tejaswini Duggaraju <naduggar@microsoft.com>
Tejesh Mehta <tejesh.mehta@gmail.com> Tejesh Mehta <tejesh.mehta@gmail.com>
terryding77 <550147740@qq.com> terryding77 <550147740@qq.com>
tgic <farmer1992@gmail.com> tgic <farmer1992@gmail.com>
@ -1811,6 +1882,7 @@ Tristan Carel <tristan@cogniteev.com>
Troy Denton <trdenton@gmail.com> Troy Denton <trdenton@gmail.com>
Tycho Andersen <tycho@docker.com> Tycho Andersen <tycho@docker.com>
Tyler Brock <tyler.brock@gmail.com> Tyler Brock <tyler.brock@gmail.com>
Tyler Brown <tylers.pile@gmail.com>
Tzu-Jung Lee <roylee17@gmail.com> Tzu-Jung Lee <roylee17@gmail.com>
uhayate <uhayate.gong@daocloud.io> uhayate <uhayate.gong@daocloud.io>
Ulysse Carion <ulyssecarion@gmail.com> Ulysse Carion <ulyssecarion@gmail.com>
@ -1871,6 +1943,7 @@ Wassim Dhif <wassimdhif@gmail.com>
Wayne Chang <wayne@neverfear.org> Wayne Chang <wayne@neverfear.org>
Wayne Song <wsong@docker.com> Wayne Song <wsong@docker.com>
Weerasak Chongnguluam <singpor@gmail.com> Weerasak Chongnguluam <singpor@gmail.com>
Wei Fu <fuweid89@gmail.com>
Wei Wu <wuwei4455@gmail.com> Wei Wu <wuwei4455@gmail.com>
Wei-Ting Kuo <waitingkuo0527@gmail.com> Wei-Ting Kuo <waitingkuo0527@gmail.com>
weipeng <weipeng@tuscloud.io> weipeng <weipeng@tuscloud.io>
@ -1900,17 +1973,23 @@ WiseTrem <shepelyov.g@gmail.com>
Wolfgang Powisch <powo@powo.priv.at> Wolfgang Powisch <powo@powo.priv.at>
Wonjun Kim <wonjun.kim@navercorp.com> Wonjun Kim <wonjun.kim@navercorp.com>
xamyzhao <x.amy.zhao@gmail.com> xamyzhao <x.amy.zhao@gmail.com>
Xian Chaobo <xianchaobo@huawei.com>
Xianglin Gao <xlgao@zju.edu.cn> Xianglin Gao <xlgao@zju.edu.cn>
Xianlu Bird <xianlubird@gmail.com> Xianlu Bird <xianlubird@gmail.com>
XiaoBing Jiang <s7v7nislands@gmail.com> XiaoBing Jiang <s7v7nislands@gmail.com>
Xiaodong Zhang <a4012017@sina.com>
Xiaoxi He <xxhe@alauda.io>
Xiaoxu Chen <chenxiaoxu14@otcaix.iscas.ac.cn> Xiaoxu Chen <chenxiaoxu14@otcaix.iscas.ac.cn>
Xiaoyu Zhang <zhang.xiaoyu33@zte.com.cn> Xiaoyu Zhang <zhang.xiaoyu33@zte.com.cn>
xichengliudui <1693291525@qq.com>
xiekeyang <xiekeyang@huawei.com> xiekeyang <xiekeyang@huawei.com>
Ximo Guanter Gonzálbez <joaquin.guantergonzalbez@telefonica.com>
Xinbo Weng <xihuanbo_0521@zju.edu.cn> Xinbo Weng <xihuanbo_0521@zju.edu.cn>
Xinzi Zhou <imdreamrunner@gmail.com> Xinzi Zhou <imdreamrunner@gmail.com>
Xiuming Chen <cc@cxm.cc> Xiuming Chen <cc@cxm.cc>
Xuecong Liao <satorulogic@gmail.com> Xuecong Liao <satorulogic@gmail.com>
xuzhaokui <cynicholas@gmail.com> xuzhaokui <cynicholas@gmail.com>
Yadnyawalkya Tale <ytale@redhat.com>
Yahya <ya7yaz@gmail.com> Yahya <ya7yaz@gmail.com>
YAMADA Tsuyoshi <tyamada@minimum2scp.org> YAMADA Tsuyoshi <tyamada@minimum2scp.org>
Yamasaki Masahide <masahide.y@gmail.com> Yamasaki Masahide <masahide.y@gmail.com>
@ -1930,6 +2009,7 @@ Yihang Ho <hoyihang5@gmail.com>
Ying Li <ying.li@docker.com> Ying Li <ying.li@docker.com>
Yohei Ueda <yohei@jp.ibm.com> Yohei Ueda <yohei@jp.ibm.com>
Yong Tang <yong.tang.github@outlook.com> Yong Tang <yong.tang.github@outlook.com>
Yongxin Li <yxli@alauda.io>
Yongzhi Pan <panyongzhi@gmail.com> Yongzhi Pan <panyongzhi@gmail.com>
Yosef Fertel <yfertel@gmail.com> Yosef Fertel <yfertel@gmail.com>
You-Sheng Yang (楊有勝) <vicamo@gmail.com> You-Sheng Yang (楊有勝) <vicamo@gmail.com>
@ -1940,9 +2020,12 @@ Yu Peng <yu.peng36@zte.com.cn>
Yu-Ju Hong <yjhong@google.com> Yu-Ju Hong <yjhong@google.com>
Yuan Sun <sunyuan3@huawei.com> Yuan Sun <sunyuan3@huawei.com>
Yuanhong Peng <pengyuanhong@huawei.com> Yuanhong Peng <pengyuanhong@huawei.com>
Yue Zhang <zy675793960@yeah.net>
Yuhao Fang <fangyuhao@gmail.com> Yuhao Fang <fangyuhao@gmail.com>
Yuichiro Kaneko <spiketeika@gmail.com>
Yunxiang Huang <hyxqshk@vip.qq.com> Yunxiang Huang <hyxqshk@vip.qq.com>
Yurii Rashkovskii <yrashk@gmail.com> Yurii Rashkovskii <yrashk@gmail.com>
Yusuf Tarık Günaydın <yusuf_tarik@hotmail.com>
Yves Junqueira <yves.junqueira@gmail.com> Yves Junqueira <yves.junqueira@gmail.com>
Zac Dover <zdover@redhat.com> Zac Dover <zdover@redhat.com>
Zach Borboa <zachborboa@gmail.com> Zach Borboa <zachborboa@gmail.com>
@ -1959,8 +2042,10 @@ ZhangHang <stevezhang2014@gmail.com>
zhangxianwei <xianwei.zw@alibaba-inc.com> zhangxianwei <xianwei.zw@alibaba-inc.com>
Zhenan Ye <21551168@zju.edu.cn> Zhenan Ye <21551168@zju.edu.cn>
zhenghenghuo <zhenghenghuo@zju.edu.cn> zhenghenghuo <zhenghenghuo@zju.edu.cn>
Zhenhai Gao <gaozh1988@live.com>
Zhenkun Bi <bi.zhenkun@zte.com.cn> Zhenkun Bi <bi.zhenkun@zte.com.cn>
Zhou Hao <zhouhao@cn.fujitsu.com> Zhou Hao <zhouhao@cn.fujitsu.com>
Zhoulin Xie <zhoulin.xie@daocloud.io>
Zhu Guihua <zhugh.fnst@cn.fujitsu.com> Zhu Guihua <zhugh.fnst@cn.fujitsu.com>
Zhu Kunjia <zhu.kunjia@zte.com.cn> Zhu Kunjia <zhu.kunjia@zte.com.cn>
Zhuoyun Wei <wzyboy@wzyboy.org> Zhuoyun Wei <wzyboy@wzyboy.org>

View File

@ -99,7 +99,7 @@ be found.
* Add `--format` option to `docker node ls` [#30424](https://github.com/docker/docker/pull/30424) * Add `--format` option to `docker node ls` [#30424](https://github.com/docker/docker/pull/30424)
* Add `--prune` option to `docker stack deploy` to remove services that are no longer defined in the docker-compose file [#31302](https://github.com/docker/docker/pull/31302) * Add `--prune` option to `docker stack deploy` to remove services that are no longer defined in the docker-compose file [#31302](https://github.com/docker/docker/pull/31302)
* Add `PORTS` column for `docker service ls` when using `ingress` mode [#30813](https://github.com/docker/docker/pull/30813) * Add `PORTS` column for `docker service ls` when using `ingress` mode [#30813](https://github.com/docker/docker/pull/30813)
- Fix unnescessary re-deploying of tasks when environment-variables are used [#32364](https://github.com/docker/docker/pull/32364) - Fix unnecessary re-deploying of tasks when environment-variables are used [#32364](https://github.com/docker/docker/pull/32364)
- Fix `docker stack deploy` not supporting `endpoint_mode` when deploying from a docker compose file [#32333](https://github.com/docker/docker/pull/32333) - Fix `docker stack deploy` not supporting `endpoint_mode` when deploying from a docker compose file [#32333](https://github.com/docker/docker/pull/32333)
- Proceed with startup if cluster component cannot be created to allow recovering from a broken swarm setup [#31631](https://github.com/docker/docker/pull/31631) - Proceed with startup if cluster component cannot be created to allow recovering from a broken swarm setup [#31631](https://github.com/docker/docker/pull/31631)

View File

@ -8,7 +8,7 @@ process](docs/contributing/).
This page contains information about reporting issues as well as some tips and This page contains information about reporting issues as well as some tips and
guidelines useful to experienced open source contributors. Finally, make sure guidelines useful to experienced open source contributors. Finally, make sure
you read our [community guidelines](#docker-community-guidelines) before you you read our [community guidelines](#moby-community-guidelines) before you
start participating. start participating.
## Topics ## Topics
@ -17,7 +17,7 @@ start participating.
* [Design and Cleanup Proposals](#design-and-cleanup-proposals) * [Design and Cleanup Proposals](#design-and-cleanup-proposals)
* [Reporting Issues](#reporting-other-issues) * [Reporting Issues](#reporting-other-issues)
* [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines) * [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines)
* [Community Guidelines](#docker-community-guidelines) * [Community Guidelines](#moby-community-guidelines)
## Reporting security issues ## Reporting security issues

View File

@ -24,18 +24,15 @@
# the case. Therefore, you don't have to disable it anymore. # the case. Therefore, you don't have to disable it anymore.
# #
FROM golang:1.10.3 AS base FROM golang:1.12.1 AS base
# FIXME(vdemeester) this is kept for other script depending on it to not fail right away
# Remove this once the other scripts uses something else to detect the version
ENV GO_VERSION 1.10.3
# allow replacing httpredir or deb mirror # allow replacing httpredir or deb mirror
ARG APT_MIRROR=deb.debian.org ARG APT_MIRROR=deb.debian.org
RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list
FROM base AS criu FROM base AS criu
# Install CRIU for checkpoint/restore support # Install CRIU for checkpoint/restore support
ENV CRIU_VERSION 3.6 ENV CRIU_VERSION 3.11
# Install dependancy packages specific to criu # Install dependency packages specific to criu
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
libnet-dev \ libnet-dev \
libprotobuf-c0-dev \ libprotobuf-c0-dev \
@ -52,11 +49,6 @@ RUN apt-get update && apt-get install -y \
&& make PREFIX=/build/ install-criu && make PREFIX=/build/ install-criu
FROM base AS registry FROM base AS registry
# Install two versions of the registry. The first is an older version that
# only supports schema1 manifests. The second is a newer version that supports
# both. This allows integration-cli tests to cover push/pull with both schema1
# and schema2 manifests.
ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd
ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827 ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827
RUN set -x \ RUN set -x \
&& export GOPATH="$(mktemp -d)" \ && export GOPATH="$(mktemp -d)" \
@ -64,20 +56,13 @@ RUN set -x \
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \ && (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \ && GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
go build -buildmode=pie -o /build/registry-v2 github.com/docker/distribution/cmd/registry \ go build -buildmode=pie -o /build/registry-v2 github.com/docker/distribution/cmd/registry \
&& case $(dpkg --print-architecture) in \
amd64|ppc64*|s390x) \
(cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1"); \
GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH"; \
go build -buildmode=pie -o /build/registry-v2-schema1 github.com/docker/distribution/cmd/registry; \
;; \
esac \
&& rm -rf "$GOPATH" && rm -rf "$GOPATH"
FROM base AS docker-py FROM base AS docker-py
# Get the "docker-py" source so we can run their integration tests # Get the "docker-py" source so we can run their integration tests
ENV DOCKER_PY_COMMIT 8b246db271a85d6541dc458838627e89c683e42f ENV DOCKER_PY_COMMIT ac922192959870774ad8428344d9faa0555f7ba6
RUN git clone https://github.com/docker/docker-py.git /build \ RUN git clone https://github.com/docker/docker-py.git /build \
&& cd /build \ && cd /build \
&& git checkout -q $DOCKER_PY_COMMIT && git checkout -q $DOCKER_PY_COMMIT
@ -118,58 +103,65 @@ FROM base AS tomlv
ENV INSTALL_BINARY_NAME=tomlv ENV INSTALL_BINARY_NAME=tomlv
COPY hack/dockerfile/install/install.sh ./install.sh COPY hack/dockerfile/install/install.sh ./install.sh
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
FROM base AS vndr FROM base AS vndr
ENV INSTALL_BINARY_NAME=vndr ENV INSTALL_BINARY_NAME=vndr
COPY hack/dockerfile/install/install.sh ./install.sh COPY hack/dockerfile/install/install.sh ./install.sh
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
FROM base AS containerd FROM base AS containerd
RUN apt-get update && apt-get install -y btrfs-tools RUN apt-get update && apt-get install -y btrfs-tools
ENV INSTALL_BINARY_NAME=containerd ENV INSTALL_BINARY_NAME=containerd
COPY hack/dockerfile/install/install.sh ./install.sh COPY hack/dockerfile/install/install.sh ./install.sh
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
FROM base AS proxy FROM base AS proxy
ENV INSTALL_BINARY_NAME=proxy ENV INSTALL_BINARY_NAME=proxy
COPY hack/dockerfile/install/install.sh ./install.sh COPY hack/dockerfile/install/install.sh ./install.sh
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
FROM base AS gometalinter FROM base AS gometalinter
ENV INSTALL_BINARY_NAME=gometalinter ENV INSTALL_BINARY_NAME=gometalinter
COPY hack/dockerfile/install/install.sh ./install.sh COPY hack/dockerfile/install/install.sh ./install.sh
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
FROM base AS dockercli FROM base AS dockercli
ENV INSTALL_BINARY_NAME=dockercli ENV INSTALL_BINARY_NAME=dockercli
COPY hack/dockerfile/install/install.sh ./install.sh COPY hack/dockerfile/install/install.sh ./install.sh
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
FROM runtime-dev AS runc FROM runtime-dev AS runc
ENV INSTALL_BINARY_NAME=runc ENV INSTALL_BINARY_NAME=runc
COPY hack/dockerfile/install/install.sh ./install.sh COPY hack/dockerfile/install/install.sh ./install.sh
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
FROM base AS tini FROM base AS tini
RUN apt-get update && apt-get install -y cmake vim-common RUN apt-get update && apt-get install -y cmake vim-common
COPY hack/dockerfile/install/install.sh ./install.sh COPY hack/dockerfile/install/install.sh ./install.sh
ENV INSTALL_BINARY_NAME=tini ENV INSTALL_BINARY_NAME=tini
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
FROM base AS rootlesskit
ENV INSTALL_BINARY_NAME=rootlesskit
COPY hack/dockerfile/install/install.sh ./install.sh
COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME
COPY ./contrib/dockerd-rootless.sh /build
# TODO: Some of this is only really needed for testing, it would be nice to split this up # TODO: Some of this is only really needed for testing, it would be nice to split this up
FROM runtime-dev AS dev FROM runtime-dev AS dev
RUN groupadd -r docker RUN groupadd -r docker
RUN useradd --create-home --gid docker unprivilegeduser RUN useradd --create-home --gid docker unprivilegeduser
# Let us use a .bashrc file
RUN ln -sfv /go/src/github.com/docker/docker/.bashrc ~/.bashrc
# Activate bash completion and include Docker's completion if mounted with DOCKER_BASH_COMPLETION_PATH # Activate bash completion and include Docker's completion if mounted with DOCKER_BASH_COMPLETION_PATH
RUN echo "source /usr/share/bash-completion/bash_completion" >> /etc/bash.bashrc RUN echo "source /usr/share/bash-completion/bash_completion" >> /etc/bash.bashrc
RUN ln -s /usr/local/completion/bash/docker /etc/bash_completion.d/docker RUN ln -s /usr/local/completion/bash/docker /etc/bash_completion.d/docker
@ -183,7 +175,11 @@ RUN apt-get update && apt-get install -y \
btrfs-tools \ btrfs-tools \
iptables \ iptables \
jq \ jq \
libcap2-bin \
libdevmapper-dev \ libdevmapper-dev \
# libffi-dev and libssl-dev appear to be required for compiling paramiko on s390x/ppc64le
libffi-dev \
libssl-dev \
libudev-dev \ libudev-dev \
libsystemd-dev \ libsystemd-dev \
binutils-mingw-w64 \ binutils-mingw-w64 \
@ -192,6 +188,8 @@ RUN apt-get update && apt-get install -y \
pigz \ pigz \
python-backports.ssl-match-hostname \ python-backports.ssl-match-hostname \
python-dev \ python-dev \
# python-cffi appears to be required for compiling paramiko on s390x/ppc64le
python-cffi \
python-mock \ python-mock \
python-pip \ python-pip \
python-requests \ python-requests \
@ -205,6 +203,9 @@ RUN apt-get update && apt-get install -y \
zip \ zip \
bzip2 \ bzip2 \
xz-utils \ xz-utils \
libprotobuf-c1 \
libnet1 \
libnl-3-200 \
--no-install-recommends --no-install-recommends
COPY --from=swagger /build/swagger* /usr/local/bin/ COPY --from=swagger /build/swagger* /usr/local/bin/
COPY --from=frozen-images /build/ /docker-frozen-images COPY --from=frozen-images /build/ /docker-frozen-images
@ -224,9 +225,12 @@ COPY --from=docker-py /build/ /docker-py
# split out into a separate image, including all the `python-*` deps installed # split out into a separate image, including all the `python-*` deps installed
# above. # above.
RUN cd /docker-py \ RUN cd /docker-py \
&& pip install docker-pycreds==0.2.1 \ && pip install docker-pycreds==0.4.0 \
&& pip install paramiko==2.4.2 \
&& pip install yamllint==1.5.0 \ && pip install yamllint==1.5.0 \
&& pip install -r test-requirements.txt && pip install -r test-requirements.txt
COPY --from=rootlesskit /build/ /usr/local/bin/
COPY --from=djs55/vpnkit@sha256:e508a17cfacc8fd39261d5b4e397df2b953690da577e2c987a47630cd0c42f8e /vpnkit /usr/local/bin/vpnkit.x86_64
ENV PATH=/usr/local/cli:$PATH ENV PATH=/usr/local/cli:$PATH
ENV DOCKER_BUILDTAGS apparmor seccomp selinux ENV DOCKER_BUILDTAGS apparmor seccomp selinux
@ -236,5 +240,7 @@ WORKDIR /go/src/github.com/docker/docker
VOLUME /var/lib/docker VOLUME /var/lib/docker
# Wrap all commands in the "docker-in-docker" script to allow nested containers # Wrap all commands in the "docker-in-docker" script to allow nested containers
ENTRYPOINT ["hack/dind"] ENTRYPOINT ["hack/dind"]
FROM dev AS final
# Upload docker source # Upload docker source
COPY . /go/src/github.com/docker/docker COPY . /go/src/github.com/docker/docker

View File

@ -1,14 +1,13 @@
## Step 1: Build tests ## Step 1: Build tests
FROM golang:1.10.3-alpine3.7 as builder FROM golang:1.12.1-alpine3.9 as builder
RUN apk add --update \ RUN apk --no-cache add \
bash \ bash \
btrfs-progs-dev \ btrfs-progs-dev \
build-base \ build-base \
curl \ curl \
lvm2-dev \ lvm2-dev \
jq \ jq
&& rm -rf /var/cache/apk/*
RUN mkdir -p /go/src/github.com/docker/docker/ RUN mkdir -p /go/src/github.com/docker/docker/
WORKDIR /go/src/github.com/docker/docker/ WORKDIR /go/src/github.com/docker/docker/
@ -40,10 +39,10 @@ RUN hack/make.sh build-integration-test-binary
RUN mkdir -p /output/tests && find . -name test.main -exec cp --parents '{}' /output/tests \; RUN mkdir -p /output/tests && find . -name test.main -exec cp --parents '{}' /output/tests \;
## Step 2: Generate testing image ## Step 2: Generate testing image
FROM alpine:3.7 as runner FROM alpine:3.8 as runner
# GNU tar is used for generating the emptyfs image # GNU tar is used for generating the emptyfs image
RUN apk add --update \ RUN apk --no-cache add \
bash \ bash \
ca-certificates \ ca-certificates \
g++ \ g++ \
@ -51,8 +50,7 @@ RUN apk add --update \
iptables \ iptables \
pigz \ pigz \
tar \ tar \
xz \ xz
&& rm -rf /var/cache/apk/*
# Add an unprivileged user to be used for tests which need it # Add an unprivileged user to be used for tests which need it
RUN addgroup docker && adduser -D -G docker unprivilegeduser -s /bin/ash RUN addgroup docker && adduser -D -G docker unprivilegeduser -s /bin/ash

View File

@ -5,7 +5,7 @@
# This represents the bare minimum required to build and test Docker. # This represents the bare minimum required to build and test Docker.
FROM debian:stretch FROM golang:1.12.1
# allow replacing httpredir or deb mirror # allow replacing httpredir or deb mirror
ARG APT_MIRROR=deb.debian.org ARG APT_MIRROR=deb.debian.org
@ -37,18 +37,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
vim-common \ vim-common \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Install Go
# IMPORTANT: If the version of Go is updated, the Windows to Linux CI machines
# will need updating, to avoid errors. Ping #docker-maintainers on IRC
# with a heads-up.
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
ENV GO_VERSION 1.10.3
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" \
| tar -xzC /usr/local
ENV PATH /go/bin:/usr/local/go/bin:$PATH
ENV GOPATH /go
ENV CGO_LDFLAGS -L/lib
# Install runc, containerd, tini and docker-proxy # Install runc, containerd, tini and docker-proxy
# Please edit hack/dockerfile/install/<name>.installer to update them. # Please edit hack/dockerfile/install/<name>.installer to update them.
COPY hack/dockerfile/install hack/dockerfile/install COPY hack/dockerfile/install hack/dockerfile/install

View File

@ -153,6 +153,13 @@
# The number of build steps below are explicitly minimised to improve performance. # The number of build steps below are explicitly minimised to improve performance.
# Extremely important - do not change the following line to reference a "specific" image,
# such as `mcr.microsoft.com/windows/servercore:ltsc2019`. If using this Dockerfile in process
# isolated containers, the kernel of the host must match the container image, and hence
# would fail between Windows Server 2016 (aka RS1) and Windows Server 2019 (aka RS5).
# It is expected that the image `microsoft/windowsservercore:latest` is present, and matches
# the hosts kernel version before doing a build.
FROM microsoft/windowsservercore FROM microsoft/windowsservercore
# Use PowerShell as the default shell # Use PowerShell as the default shell
@ -161,7 +168,7 @@ SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPref
# Environment variable notes: # Environment variable notes:
# - GO_VERSION must be consistent with 'Dockerfile' used by Linux. # - GO_VERSION must be consistent with 'Dockerfile' used by Linux.
# - FROM_DOCKERFILE is used for detection of building within a container. # - FROM_DOCKERFILE is used for detection of building within a container.
ENV GO_VERSION=1.10.3 ` ENV GO_VERSION=1.12.1 `
GIT_VERSION=2.11.1 ` GIT_VERSION=2.11.1 `
GOPATH=C:\go ` GOPATH=C:\go `
FROM_DOCKERFILE=1 FROM_DOCKERFILE=1

325
vendor/github.com/docker/docker/Jenkinsfile generated vendored Normal file
View File

@ -0,0 +1,325 @@
def withGithubStatus(String context, Closure cl) {
def setGithubStatus = { String state ->
try {
def backref = "${BUILD_URL}flowGraphTable/"
def reposSourceURL = scm.repositories[0].getURIs()[0].toString()
step(
$class: 'GitHubCommitStatusSetter',
contextSource: [$class: "ManuallyEnteredCommitContextSource", context: context],
errorHandlers: [[$class: 'ShallowAnyErrorHandler']],
reposSource: [$class: 'ManuallyEnteredRepositorySource', url: reposSourceURL],
statusBackrefSource: [$class: 'ManuallyEnteredBackrefSource', backref: backref],
statusResultSource: [$class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', state: state]]],
)
} catch (err) {
echo "Exception from GitHubCommitStatusSetter for $context: $err"
}
}
setGithubStatus 'PENDING'
try {
cl()
} catch (err) {
// AbortException signals a "normal" build failure.
if (!(err instanceof hudson.AbortException)) {
echo "Exception in withGithubStatus for $context: $err"
}
setGithubStatus 'FAILURE'
throw err
}
setGithubStatus 'SUCCESS'
}
pipeline {
agent none
options {
buildDiscarder(logRotator(daysToKeepStr: '30'))
timeout(time: 3, unit: 'HOURS')
}
parameters {
booleanParam(name: 'janky', defaultValue: true, description: 'x86 Build/Test')
booleanParam(name: 'experimental', defaultValue: true, description: 'x86 Experimental Build/Test ')
booleanParam(name: 'z', defaultValue: true, description: 'IBM Z (s390x) Build/Test')
booleanParam(name: 'powerpc', defaultValue: true, description: 'PowerPC (ppc64le) Build/Test')
booleanParam(name: 'vendor', defaultValue: true, description: 'Vendor')
booleanParam(name: 'windowsRS1', defaultValue: true, description: 'Windows 2016 (RS1) Build/Test')
booleanParam(name: 'windowsRS5', defaultValue: true, description: 'Windows 2019 (RS5) Build/Test')
}
stages {
stage('Build') {
parallel {
stage('janky') {
when {
beforeAgent true
expression { params.janky }
}
agent {
node {
label 'ubuntu-1604-overlay2-stable'
}
}
steps {
withCredentials([string(credentialsId: '52af932f-f13f-429e-8467-e7ff8b965cdb', variable: 'CODECOV_TOKEN')]) {
withGithubStatus('janky') {
sh '''
# todo: include ip_vs in base image
sudo modprobe ip_vs
GITCOMMIT=$(git rev-parse --short HEAD)
docker build --rm --force-rm --build-arg APT_MIRROR=cdn-fastly.deb.debian.org -t docker:$GITCOMMIT .
docker run --rm -t --privileged \
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
-v "$WORKSPACE/.git:/go/src/github.com/docker/docker/.git" \
--name docker-pr$BUILD_NUMBER \
-e DOCKER_GITCOMMIT=${GITCOMMIT} \
-e DOCKER_GRAPHDRIVER=vfs \
-e DOCKER_EXECDRIVER=native \
-e CODECOV_TOKEN \
-e GIT_SHA1=${GIT_COMMIT} \
docker:$GITCOMMIT \
hack/ci/janky
'''
sh '''
GITCOMMIT=$(git rev-parse --short HEAD)
echo "Building e2e image"
docker build --build-arg DOCKER_GITCOMMIT=$GITCOMMIT -t moby-e2e-test -f Dockerfile.e2e .
'''
}
}
}
post {
always {
sh '''
echo "Ensuring container killed."
docker rm -vf docker-pr$BUILD_NUMBER || true
echo "Chowning /workspace to jenkins user"
docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace
'''
sh '''
echo "Creating bundles.tar.gz"
(find bundles -name '*.log' -o -name '*.prof' -o -name integration.test | xargs tar -czf bundles.tar.gz) || true
'''
archiveArtifacts artifacts: 'bundles.tar.gz'
}
}
}
stage('experimental') {
when {
beforeAgent true
expression { params.experimental }
}
agent {
node {
label 'ubuntu-1604-aufs-stable'
}
}
steps {
withGithubStatus('experimental') {
sh '''
GITCOMMIT=$(git rev-parse --short HEAD)
docker build --rm --force-rm --build-arg APT_MIRROR=cdn-fastly.deb.debian.org -t docker:${GITCOMMIT}-exp .
docker run --rm -t --privileged \
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
-e DOCKER_EXPERIMENTAL=y \
--name docker-pr-exp$BUILD_NUMBER \
-e DOCKER_GITCOMMIT=${GITCOMMIT} \
-e DOCKER_GRAPHDRIVER=vfs \
-e DOCKER_EXECDRIVER=native \
docker:${GITCOMMIT}-exp \
hack/ci/experimental
'''
}
}
post {
always {
sh '''
echo "Ensuring container killed."
docker rm -vf docker-pr-exp$BUILD_NUMBER || true
echo "Chowning /workspace to jenkins user"
docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace
'''
sh '''
echo "Creating bundles.tar.gz"
(find bundles -name '*.log' -o -name '*.prof' -o -name integration.test | xargs tar -czf bundles.tar.gz) || true
'''
archiveArtifacts artifacts: 'bundles.tar.gz'
}
}
}
stage('z') {
when {
beforeAgent true
expression { params.z }
}
agent {
node {
label 's390x-ubuntu-1604'
}
}
steps {
withGithubStatus('z') {
sh '''
GITCOMMIT=$(git rev-parse --short HEAD)
test -f Dockerfile.s390x && \
docker build --rm --force-rm --build-arg APT_MIRROR=cdn-fastly.deb.debian.org -t docker-s390x:$GITCOMMIT -f Dockerfile.s390x . || \
docker build --rm --force-rm --build-arg APT_MIRROR=cdn-fastly.deb.debian.org -t docker-s390x:$GITCOMMIT -f Dockerfile .
docker run --rm -t --privileged \
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
--name docker-pr-s390x$BUILD_NUMBER \
-e DOCKER_GRAPHDRIVER=vfs \
-e DOCKER_EXECDRIVER=native \
-e TIMEOUT="300m" \
-e DOCKER_GITCOMMIT=${GITCOMMIT} \
docker-s390x:$GITCOMMIT \
hack/ci/z
'''
}
}
post {
always {
sh '''
echo "Ensuring container killed."
docker rm -vf docker-pr-s390x$BUILD_NUMBER || true
echo "Chowning /workspace to jenkins user"
docker run --rm -v "$WORKSPACE:/workspace" s390x/busybox chown -R "$(id -u):$(id -g)" /workspace
'''
sh '''
echo "Creating bundles.tar.gz"
find bundles -name '*.log' | xargs tar -czf bundles.tar.gz
'''
archiveArtifacts artifacts: 'bundles.tar.gz'
}
}
}
stage('powerpc') {
when {
beforeAgent true
expression { params.powerpc }
}
agent {
node {
label 'ppc64le-ubuntu-1604'
}
}
steps {
withGithubStatus('powerpc') {
sh '''
GITCOMMIT=$(git rev-parse --short HEAD)
test -f Dockerfile.ppc64le && \
docker build --rm --force-rm --build-arg APT_MIRROR=cdn-fastly.deb.debian.org -t docker-powerpc:$GITCOMMIT -f Dockerfile.ppc64le . || \
docker build --rm --force-rm --build-arg APT_MIRROR=cdn-fastly.deb.debian.org -t docker-powerpc:$GITCOMMIT -f Dockerfile .
docker run --rm -t --privileged \
-v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \
--name docker-pr-power$BUILD_NUMBER \
-e DOCKER_GRAPHDRIVER=vfs \
-e DOCKER_EXECDRIVER=native \
-e DOCKER_GITCOMMIT=${GITCOMMIT} \
-e TIMEOUT="180m" \
docker-powerpc:$GITCOMMIT \
hack/ci/powerpc
'''
}
}
post {
always {
sh '''
echo "Ensuring container killed."
docker rm -vf docker-pr-power$BUILD_NUMBER || true
echo "Chowning /workspace to jenkins user"
docker run --rm -v "$WORKSPACE:/workspace" ppc64le/busybox chown -R "$(id -u):$(id -g)" /workspace
'''
sh '''
echo "Creating bundles.tar.gz"
find bundles -name '*.log' | xargs tar -czf bundles.tar.gz
'''
archiveArtifacts artifacts: 'bundles.tar.gz'
}
}
}
stage('vendor') {
when {
beforeAgent true
expression { params.vendor }
}
agent {
node {
label 'ubuntu-1604-aufs-stable'
}
}
steps {
withGithubStatus('vendor') {
sh '''
GITCOMMIT=$(git rev-parse --short HEAD)
docker build --rm --force-rm --build-arg APT_MIRROR=cdn-fastly.deb.debian.org -t dockerven:$GITCOMMIT .
docker run --rm -t --privileged \
--name dockerven-pr$BUILD_NUMBER \
-e DOCKER_GRAPHDRIVER=vfs \
-e DOCKER_EXECDRIVER=native \
-v "$WORKSPACE/.git:/go/src/github.com/docker/docker/.git" \
-e DOCKER_GITCOMMIT=${GITCOMMIT} \
-e TIMEOUT=120m dockerven:$GITCOMMIT \
hack/validate/vendor
'''
}
}
}
stage('windowsRS1') {
when {
beforeAgent true
expression { params.windowsRS1 }
}
agent {
node {
label 'windows-rs1'
customWorkspace 'c:\\gopath\\src\\github.com\\docker\\docker'
}
}
steps {
withGithubStatus('windowsRS1') {
powershell '''
$ErrorActionPreference = 'Stop'
.\\hack\\ci\\windows.ps1
exit $LastExitCode
'''
}
}
}
stage('windowsRS5-process') {
when {
beforeAgent true
expression { params.windowsRS5 }
}
agent {
node {
label 'windows-rs5'
customWorkspace 'c:\\gopath\\src\\github.com\\docker\\docker'
}
}
steps {
withGithubStatus('windowsRS5-process') {
powershell '''
$ErrorActionPreference = 'Stop'
.\\hack\\ci\\windows.ps1
exit $LastExitCode
'''
}
}
}
}
}
}
}

View File

@ -176,7 +176,7 @@
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
Copyright 2013-2017 Docker, Inc. Copyright 2013-2018 Docker, Inc.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -36,6 +36,7 @@
"jhowardmsft", "jhowardmsft",
"johnstep", "johnstep",
"justincormack", "justincormack",
"kolyshkin",
"mhbauer", "mhbauer",
"mlaventure", "mlaventure",
"runcom", "runcom",
@ -50,15 +51,6 @@
"yongtang" "yongtang"
] ]
[Org."Docs maintainers"]
# TODO Describe the docs maintainers role.
people = [
"misty",
"thajeztah"
]
[Org.Curators] [Org.Curators]
# The curators help ensure that incoming issues and pull requests are properly triaged and # The curators help ensure that incoming issues and pull requests are properly triaged and
@ -78,6 +70,7 @@
"chanwit", "chanwit",
"fntlnz", "fntlnz",
"gianarb", "gianarb",
"olljanat",
"programmerq", "programmerq",
"rheinwein", "rheinwein",
"ripcurld", "ripcurld",
@ -162,7 +155,7 @@
# Alexander Morozov contributed many features to Docker, worked on the premise of # Alexander Morozov contributed many features to Docker, worked on the premise of
# what later became containerd (and worked on that too), and made a "stupid" Go # what later became containerd (and worked on that too), and made a "stupid" Go
# vendor tool specificaly for docker/docker needs: vndr (https://github.com/LK4D4/vndr). # vendor tool specifically for docker/docker needs: vndr (https://github.com/LK4D4/vndr).
# Not many know that Alexander is a master negotiator, being able to change course # Not many know that Alexander is a master negotiator, being able to change course
# of action with a single "Nope, we're not gonna do that". # of action with a single "Nope, we're not gonna do that".
"lk4d4", "lk4d4",
@ -365,6 +358,11 @@
Email = "justin.cormack@docker.com" Email = "justin.cormack@docker.com"
GitHub = "justincormack" GitHub = "justincormack"
[people.kolyshkin]
Name = "Kir Kolyshkin"
Email = "kolyshkin@gmail.com"
GitHub = "kolyshkin"
[people.lk4d4] [people.lk4d4]
Name = "Alexander Morozov" Name = "Alexander Morozov"
Email = "lk4d4@docker.com" Email = "lk4d4@docker.com"
@ -380,11 +378,6 @@
Email = "mbauer@us.ibm.com" Email = "mbauer@us.ibm.com"
GitHub = "mhbauer" GitHub = "mhbauer"
[people.misty]
Name = "Misty Stanley-Jones"
Email = "misty@docker.com"
GitHub = "mistyhacks"
[people.mlaventure] [people.mlaventure]
Name = "Kenfe-Mickaël Laventure" Name = "Kenfe-Mickaël Laventure"
Email = "mickael.laventure@gmail.com" Email = "mickael.laventure@gmail.com"
@ -400,6 +393,11 @@
Email = "mrjana@docker.com" Email = "mrjana@docker.com"
GitHub = "mrjana" GitHub = "mrjana"
[people.olljanat]
Name = "Olli Janatuinen"
Email = "olli.janatuinen@gmail.com"
GitHub = "olljanat"
[people.programmerq] [people.programmerq]
Name = "Jeff Anderson" Name = "Jeff Anderson"
Email = "jeff@docker.com" Email = "jeff@docker.com"

View File

@ -1,10 +1,8 @@
.PHONY: all binary dynbinary build cross help init-go-pkg-cache install manpages run shell test test-docker-py test-integration test-unit validate win .PHONY: all binary dynbinary build cross help install manpages run shell test test-docker-py test-integration test-unit validate win
# set the graph driver as the current graphdriver if not set # set the graph driver as the current graphdriver if not set
DOCKER_GRAPHDRIVER := $(if $(DOCKER_GRAPHDRIVER),$(DOCKER_GRAPHDRIVER),$(shell docker info 2>&1 | grep "Storage Driver" | sed 's/.*: //')) DOCKER_GRAPHDRIVER := $(if $(DOCKER_GRAPHDRIVER),$(DOCKER_GRAPHDRIVER),$(shell docker info 2>&1 | grep "Storage Driver" | sed 's/.*: //'))
export DOCKER_GRAPHDRIVER export DOCKER_GRAPHDRIVER
DOCKER_INCREMENTAL_BINARY := $(if $(DOCKER_INCREMENTAL_BINARY),$(DOCKER_INCREMENTAL_BINARY),1)
export DOCKER_INCREMENTAL_BINARY
# get OS/Arch of docker engine # get OS/Arch of docker engine
DOCKER_OSARCH := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $${DOCKER_ENGINE_OSARCH}') DOCKER_OSARCH := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $${DOCKER_ENGINE_OSARCH}')
@ -13,6 +11,12 @@ DOCKERFILE := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $$
DOCKER_GITCOMMIT := $(shell git rev-parse --short HEAD || echo unsupported) DOCKER_GITCOMMIT := $(shell git rev-parse --short HEAD || echo unsupported)
export DOCKER_GITCOMMIT export DOCKER_GITCOMMIT
# allow overriding the repository and branch that validation scripts are running
# against these are used in hack/validate/.validate to check what changed in the PR.
export VALIDATE_REPO
export VALIDATE_BRANCH
export VALIDATE_ORIGIN_BRANCH
# env vars passed through directly to Docker's build scripts # env vars passed through directly to Docker's build scripts
# to allow things like `make KEEPBUNDLE=1 binary` easily # to allow things like `make KEEPBUNDLE=1 binary` easily
# `project/PACKAGERS.md` have some limited documentation of some of these # `project/PACKAGERS.md` have some limited documentation of some of these
@ -30,14 +34,15 @@ DOCKER_ENVS := \
-e KEEPBUNDLE \ -e KEEPBUNDLE \
-e DOCKER_BUILD_ARGS \ -e DOCKER_BUILD_ARGS \
-e DOCKER_BUILD_GOGC \ -e DOCKER_BUILD_GOGC \
-e DOCKER_BUILD_OPTS \
-e DOCKER_BUILD_PKGS \ -e DOCKER_BUILD_PKGS \
-e DOCKER_BUILDKIT \
-e DOCKER_BASH_COMPLETION_PATH \ -e DOCKER_BASH_COMPLETION_PATH \
-e DOCKER_CLI_PATH \ -e DOCKER_CLI_PATH \
-e DOCKER_DEBUG \ -e DOCKER_DEBUG \
-e DOCKER_EXPERIMENTAL \ -e DOCKER_EXPERIMENTAL \
-e DOCKER_GITCOMMIT \ -e DOCKER_GITCOMMIT \
-e DOCKER_GRAPHDRIVER \ -e DOCKER_GRAPHDRIVER \
-e DOCKER_INCREMENTAL_BINARY \
-e DOCKER_LDFLAGS \ -e DOCKER_LDFLAGS \
-e DOCKER_PORT \ -e DOCKER_PORT \
-e DOCKER_REMAP_ROOT \ -e DOCKER_REMAP_ROOT \
@ -48,6 +53,9 @@ DOCKER_ENVS := \
-e TESTDIRS \ -e TESTDIRS \
-e TESTFLAGS \ -e TESTFLAGS \
-e TIMEOUT \ -e TIMEOUT \
-e VALIDATE_REPO \
-e VALIDATE_BRANCH \
-e VALIDATE_ORIGIN_BRANCH \
-e HTTP_PROXY \ -e HTTP_PROXY \
-e HTTPS_PROXY \ -e HTTPS_PROXY \
-e NO_PROXY \ -e NO_PROXY \
@ -55,13 +63,18 @@ DOCKER_ENVS := \
-e https_proxy \ -e https_proxy \
-e no_proxy \ -e no_proxy \
-e VERSION \ -e VERSION \
-e PLATFORM -e PLATFORM \
-e DEFAULT_PRODUCT_LICENSE \
-e PRODUCT
# note: we _cannot_ add "-e DOCKER_BUILDTAGS" here because even if it's unset in the shell, that would shadow the "ENV DOCKER_BUILDTAGS" set in our Dockerfile, which is very important for our official builds # note: we _cannot_ add "-e DOCKER_BUILDTAGS" here because even if it's unset in the shell, that would shadow the "ENV DOCKER_BUILDTAGS" set in our Dockerfile, which is very important for our official builds
# to allow `make BIND_DIR=. shell` or `make BIND_DIR= test` # to allow `make BIND_DIR=. shell` or `make BIND_DIR= test`
# (default to no bind mount if DOCKER_HOST is set) # (default to no bind mount if DOCKER_HOST is set)
# note: BINDDIR is supported for backwards-compatibility here # note: BINDDIR is supported for backwards-compatibility here
BIND_DIR := $(if $(BINDDIR),$(BINDDIR),$(if $(DOCKER_HOST),,bundles)) BIND_DIR := $(if $(BINDDIR),$(BINDDIR),$(if $(DOCKER_HOST),,bundles))
# DOCKER_MOUNT can be overriden, but use at your own risk!
ifndef DOCKER_MOUNT
DOCKER_MOUNT := $(if $(BIND_DIR),-v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/docker/docker/$(BIND_DIR)") DOCKER_MOUNT := $(if $(BIND_DIR),-v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/docker/docker/$(BIND_DIR)")
# This allows the test suite to be able to run without worrying about the underlying fs used by the container running the daemon (e.g. aufs-on-aufs), so long as the host running the container is running a supported fs. # This allows the test suite to be able to run without worrying about the underlying fs used by the container running the daemon (e.g. aufs-on-aufs), so long as the host running the container is running a supported fs.
@ -69,17 +82,14 @@ DOCKER_MOUNT := $(if $(BIND_DIR),-v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/do
# Note that `BIND_DIR` will already be set to `bundles` if `DOCKER_HOST` is not set (see above BIND_DIR line), in such case this will do nothing since `DOCKER_MOUNT` will already be set. # Note that `BIND_DIR` will already be set to `bundles` if `DOCKER_HOST` is not set (see above BIND_DIR line), in such case this will do nothing since `DOCKER_MOUNT` will already be set.
DOCKER_MOUNT := $(if $(DOCKER_MOUNT),$(DOCKER_MOUNT),-v /go/src/github.com/docker/docker/bundles) -v "$(CURDIR)/.git:/go/src/github.com/docker/docker/.git" DOCKER_MOUNT := $(if $(DOCKER_MOUNT),$(DOCKER_MOUNT),-v /go/src/github.com/docker/docker/bundles) -v "$(CURDIR)/.git:/go/src/github.com/docker/docker/.git"
# This allows to set the docker-dev container name DOCKER_MOUNT_CACHE := -v docker-dev-cache:/root/.cache
DOCKER_CONTAINER_NAME := $(if $(CONTAINER_NAME),--name $(CONTAINER_NAME),)
# enable package cache if DOCKER_INCREMENTAL_BINARY and DOCKER_MOUNT (i.e.DOCKER_HOST) are set
PKGCACHE_MAP := gopath:/go/pkg goroot-linux_amd64:/usr/local/go/pkg/linux_amd64 goroot-linux_amd64_netgo:/usr/local/go/pkg/linux_amd64_netgo
PKGCACHE_VOLROOT := dockerdev-go-pkg-cache
PKGCACHE_VOL := $(if $(PKGCACHE_DIR),$(CURDIR)/$(PKGCACHE_DIR)/,$(PKGCACHE_VOLROOT)-)
DOCKER_MOUNT_PKGCACHE := $(if $(DOCKER_INCREMENTAL_BINARY),$(shell echo $(PKGCACHE_MAP) | sed -E 's@([^ ]*)@-v "$(PKGCACHE_VOL)\1"@g'),)
DOCKER_MOUNT_CLI := $(if $(DOCKER_CLI_PATH),-v $(shell dirname $(DOCKER_CLI_PATH)):/usr/local/cli,) DOCKER_MOUNT_CLI := $(if $(DOCKER_CLI_PATH),-v $(shell dirname $(DOCKER_CLI_PATH)):/usr/local/cli,)
DOCKER_MOUNT_BASH_COMPLETION := $(if $(DOCKER_BASH_COMPLETION_PATH),-v $(shell dirname $(DOCKER_BASH_COMPLETION_PATH)):/usr/local/completion/bash,) DOCKER_MOUNT_BASH_COMPLETION := $(if $(DOCKER_BASH_COMPLETION_PATH),-v $(shell dirname $(DOCKER_BASH_COMPLETION_PATH)):/usr/local/completion/bash,)
DOCKER_MOUNT := $(DOCKER_MOUNT) $(DOCKER_MOUNT_PKGCACHE) $(DOCKER_MOUNT_CLI) $(DOCKER_MOUNT_BASH_COMPLETION) DOCKER_MOUNT := $(DOCKER_MOUNT) $(DOCKER_MOUNT_CACHE) $(DOCKER_MOUNT_CLI) $(DOCKER_MOUNT_BASH_COMPLETION)
endif # ifndef DOCKER_MOUNT
# This allows to set the docker-dev container name
DOCKER_CONTAINER_NAME := $(if $(CONTAINER_NAME),--name $(CONTAINER_NAME),)
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null) GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
GIT_BRANCH_CLEAN := $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g") GIT_BRANCH_CLEAN := $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g")
@ -107,6 +117,9 @@ INTERACTIVE := $(shell [ -t 0 ] && echo 1 || echo 0)
ifeq ($(INTERACTIVE), 1) ifeq ($(INTERACTIVE), 1)
DOCKER_FLAGS += -t DOCKER_FLAGS += -t
endif endif
ifeq ($(BIND_DIR), .)
DOCKER_BUILD_OPTS += --target=dev
endif
DOCKER_RUN_DOCKER := $(DOCKER_FLAGS) "$(DOCKER_IMAGE)" DOCKER_RUN_DOCKER := $(DOCKER_FLAGS) "$(DOCKER_IMAGE)"
@ -121,28 +134,26 @@ binary: build ## build the linux binaries
dynbinary: build ## build the linux dynbinaries dynbinary: build ## build the linux dynbinaries
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary $(DOCKER_RUN_DOCKER) hack/make.sh dynbinary
build: bundles init-go-pkg-cache build: DOCKER_BUILDKIT ?= 1
build: bundles
$(warning The docker client CLI has moved to github.com/docker/cli. For a dev-test cycle involving the CLI, run:${\n} DOCKER_CLI_PATH=/host/path/to/cli/binary make shell ${\n} then change the cli and compile into a binary at the same location.${\n}) $(warning The docker client CLI has moved to github.com/docker/cli. For a dev-test cycle involving the CLI, run:${\n} DOCKER_CLI_PATH=/host/path/to/cli/binary make shell ${\n} then change the cli and compile into a binary at the same location.${\n})
docker build ${BUILD_APT_MIRROR} ${DOCKER_BUILD_ARGS} -t "$(DOCKER_IMAGE)" -f "$(DOCKERFILE)" . DOCKER_BUILDKIT="${DOCKER_BUILDKIT}" docker build ${BUILD_APT_MIRROR} ${DOCKER_BUILD_ARGS} ${DOCKER_BUILD_OPTS} -t "$(DOCKER_IMAGE)" -f "$(DOCKERFILE)" .
bundles: bundles:
mkdir bundles mkdir bundles
clean: clean-pkg-cache-vol ## clean up cached resources .PHONY: clean
clean: clean-cache
clean-pkg-cache-vol: .PHONY: clean-cache
@- $(foreach mapping,$(PKGCACHE_MAP), \ clean-cache:
$(shell docker volume rm $(PKGCACHE_VOLROOT)-$(shell echo $(mapping) | awk -F':/' '{ print $$1 }') > /dev/null 2>&1) \ docker volume rm -f docker-dev-cache
)
cross: build ## cross build the binaries for darwin, freebsd and\nwindows cross: build ## cross build the binaries for darwin, freebsd and\nwindows
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary binary cross $(DOCKER_RUN_DOCKER) hack/make.sh dynbinary binary cross
help: ## this help help: ## this help
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {gsub("\\\\n",sprintf("\n%22c",""), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
init-go-pkg-cache:
$(if $(PKGCACHE_DIR), mkdir -p $(shell echo $(PKGCACHE_MAP) | sed -E 's@([^: ]*):[^ ]*@$(PKGCACHE_DIR)/\1@g'))
install: ## install the linux binaries install: ## install the linux binaries
KEEPBUNDLE=1 hack/make.sh install-binary KEEPBUNDLE=1 hack/make.sh install-binary
@ -164,6 +175,9 @@ test-integration-cli: test-integration ## (DEPRECATED) use test-integration
test-integration: build ## run the integration tests test-integration: build ## run the integration tests
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-integration $(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-integration
test-integration-flaky: build ## run the stress test for all new integration tests
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-integration-flaky
test-unit: build ## run the unit tests test-unit: build ## run the unit tests
$(DOCKER_RUN_DOCKER) hack/test/unit $(DOCKER_RUN_DOCKER) hack/test/unit
@ -171,7 +185,7 @@ validate: build ## validate DCO, Seccomp profile generation, gofmt,\n./pkg/ isol
$(DOCKER_RUN_DOCKER) hack/validate/all $(DOCKER_RUN_DOCKER) hack/validate/all
win: build ## cross build the binary for windows win: build ## cross build the binary for windows
$(DOCKER_RUN_DOCKER) hack/make.sh win $(DOCKER_RUN_DOCKER) DOCKER_CROSSPLATFORMS=windows/amd64 hack/make.sh cross
.PHONY: swagger-gen .PHONY: swagger-gen
swagger-gen: swagger-gen:
@ -194,12 +208,11 @@ build-integration-cli-on-swarm: build ## build images and binary for running int
go build -buildmode=pie -o ./hack/integration-cli-on-swarm/integration-cli-on-swarm ./hack/integration-cli-on-swarm/host go build -buildmode=pie -o ./hack/integration-cli-on-swarm/integration-cli-on-swarm ./hack/integration-cli-on-swarm/host
@echo "Building $(INTEGRATION_CLI_MASTER_IMAGE)" @echo "Building $(INTEGRATION_CLI_MASTER_IMAGE)"
docker build -t $(INTEGRATION_CLI_MASTER_IMAGE) hack/integration-cli-on-swarm/agent docker build -t $(INTEGRATION_CLI_MASTER_IMAGE) hack/integration-cli-on-swarm/agent
# For worker, we don't use `docker build` so as to enable DOCKER_INCREMENTAL_BINARY and so on
@echo "Building $(INTEGRATION_CLI_WORKER_IMAGE) from $(DOCKER_IMAGE)" @echo "Building $(INTEGRATION_CLI_WORKER_IMAGE) from $(DOCKER_IMAGE)"
$(eval tmp := integration-cli-worker-tmp) $(eval tmp := integration-cli-worker-tmp)
# We mount pkgcache, but not bundle (bundle needs to be baked into the image) # We mount pkgcache, but not bundle (bundle needs to be baked into the image)
# For avoiding bakings DOCKER_GRAPHDRIVER and so on to image, we cannot use $(DOCKER_ENVS) here # For avoiding bakings DOCKER_GRAPHDRIVER and so on to image, we cannot use $(DOCKER_ENVS) here
docker run -t -d --name $(tmp) -e DOCKER_GITCOMMIT -e BUILDFLAGS -e DOCKER_INCREMENTAL_BINARY --privileged $(DOCKER_MOUNT_PKGCACHE) $(DOCKER_IMAGE) top docker run -t -d --name $(tmp) -e DOCKER_GITCOMMIT -e BUILDFLAGS --privileged $(DOCKER_IMAGE) top
docker exec $(tmp) hack/make.sh build-integration-test-binary dynbinary docker exec $(tmp) hack/make.sh build-integration-test-binary dynbinary
docker exec $(tmp) go build -buildmode=pie -o /worker github.com/docker/docker/hack/integration-cli-on-swarm/agent/worker docker exec $(tmp) go build -buildmode=pie -o /worker github.com/docker/docker/hack/integration-cli-on-swarm/agent/worker
docker commit -c 'ENTRYPOINT ["/worker"]' $(tmp) $(INTEGRATION_CLI_WORKER_IMAGE) docker commit -c 'ENTRYPOINT ["/worker"]' $(tmp) $(INTEGRATION_CLI_WORKER_IMAGE)

View File

@ -35,34 +35,83 @@ issue, in the Slack channel, or in person at the Moby Summits that happen every
## 1.1 Runtime improvements ## 1.1 Runtime improvements
We introduced [`runC`](https://runc.io) as a standalone low-level tool for container Over time we have accumulated a lot of functionality in the container runtime
execution in 2015, the first stage in spinning out parts of the Engine into standalone tools. aspect of Moby while also growing in other areas. Much of the container runtime
pieces are now duplicated work available in other, lower level components such
as [containerd](https://containerd.io).
As runC continued evolving, and the OCI specification along with it, we created Moby currently only utilizes containerd for basic runtime state management, e.g. starting
[`containerd`](https://github.com/containerd/containerd), a daemon to control and monitor `runC`. and stopping a container, which is what the pre-containerd 1.0 daemon provided.
In late 2016 this was relaunched as the `containerd` 1.0 track, aiming to provide a common runtime Now that containerd is a full-fledged container runtime which supports full
for the whole spectrum of container systems, including Kubernetes, with wide community support. container life-cycle management, we would like to start relying more on containerd
This change meant that there was an increased scope for `containerd`, including image management and removing the bits in Moby which are now duplicated. This will necessitate
and storage drivers. a significant effort to refactor and even remove large parts of Moby's codebase.
Moby will rely on a long-running `containerd` companion daemon for all container execution Tracking issues:
related operations. This could open the door in the future for Engine restarts without interrupting
running containers. The switch over to containerd 1.0 is an important goal for the project, and
will result in a significant simplification of the functions implemented in this repository.
## 1.2 Internal decoupling - [#38043](https://github.com/moby/moby/issues/38043) Proposal: containerd image integration
## 1.2 Image Builder
Work is ongoing to integrate [BuildKit](https://github.com/moby/buildkit) into
Moby and replace the "v0" build implementation. Buildkit offers better cache
management, parallelizable build steps, and better extensibility while also
keeping builds portable, a chief tenent of Moby's builder.
Upon completion of this effort, users will have a builder that performs better
while also being more extensible, enabling users to provide their own custom
syntax which can be either Dockerfile-like or something completely different.
See [buildpacks on buildkit](https://github.com/tonistiigi/buildkit-pack) as an
example of this extensibility.
New features for the builder and Dockerfile should be implemented first in the
BuildKit backend using an external Dockerfile implementation from the container
images. This allows everyone to test and evaluate the feature without upgrading
their daemon. New features should go to the experimental channel first, and can be
part of the `docker/dockerfile:experimental` image. From there they graduate to
`docker/dockerfile:latest` and binary releases. The Dockerfile frontend source
code is temporarily located at
[https://github.com/moby/buildkit/tree/master/frontend/dockerfile](https://github.com/moby/buildkit/tree/master/frontend/dockerfile)
with separate new features defined with go build tags.
Tracking issues:
- [#32925](https://github.com/moby/moby/issues/32925) discussion: builder future: buildkit
## 1.3 Rootless Mode
Running the daemon requires elevated privileges for many tasks. We would like to
support running the daemon as a normal, unprivileged user without requiring `suid`
binaries.
Tracking issues:
- [#37375](https://github.com/moby/moby/issues/37375) Proposal: allow running `dockerd` as an unprivileged user (aka rootless mode)
## 1.4 Testing
Moby has many tests, both unit and integration. Moby needs more tests which can
cover the full spectrum functionality and edge cases out there.
Tests in the `integration-cli` folder should also be migrated into (both in
location and style) the `integration` folder. These newer tests are simpler to
run in isolation, simpler to read, simpler to write, and more fully exercise the
API. Meanwhile tests of the docker CLI should generally live in docker/cli.
Tracking issues:
- [#32866](https://github.com/moby/moby/issues/32866) Replace integration-cli suite with API test suite
## 1.5 Internal decoupling
A lot of work has been done in trying to decouple Moby internals. This process of creating A lot of work has been done in trying to decouple Moby internals. This process of creating
standalone projects with a well defined function that attract a dedicated community should continue. standalone projects with a well defined function that attract a dedicated community should continue.
As well as integrating `containerd` we would like to integrate [BuildKit](https://github.com/moby/buildkit) As well as integrating `containerd` we would like to integrate [BuildKit](https://github.com/moby/buildkit)
as the next standalone component. as the next standalone component.
We see gRPC as the natural communication layer between decoupled components. We see gRPC as the natural communication layer between decoupled components.
## 1.3 Custom assembly tooling In addition to pushing out large components into other projects, much of the
internal code structure, and in particular the
We have been prototyping the Moby [assembly tool](https://github.com/moby/tool) which was originally ["Daemon"](https://godoc.org/github.com/docker/docker/daemon#Daemon) object,
developed for LinuxKit and intend to turn it into a more generic packaging and assembly mechanism should be split into smaller, more manageable, and more testable components.
that can build not only the default version of Moby, as distribution packages or other useful forms,
but can also build very different container systems, themselves built of cooperating daemons built in
and running in containers. We intend to merge this functionality into this repo.

View File

@ -8,11 +8,11 @@ questions you may have as an aspiring Moby contributor.
Moby has two test suites (and one legacy test suite): Moby has two test suites (and one legacy test suite):
* Unit tests - use standard `go test` and * Unit tests - use standard `go test` and
[gotestyourself/assert](https://godoc.org/github.com/gotestyourself/gotestyourself/assert) assertions. They are located in [gotest.tools/assert](https://godoc.org/gotest.tools/assert) assertions. They are located in
the package they test. Unit tests should be fast and test only their own the package they test. Unit tests should be fast and test only their own
package. package.
* API integration tests - use standard `go test` and * API integration tests - use standard `go test` and
[gotestyourself/assert](https://godoc.org/github.com/gotestyourself/gotestyourself/assert) assertions. They are located in [gotest.tools/assert](https://godoc.org/gotest.tools/assert) assertions. They are located in
`./integration/<component>` directories, where `component` is: container, `./integration/<component>` directories, where `component` is: container,
image, volume, etc. These tests perform HTTP requests to an API endpoint and image, volume, etc. These tests perform HTTP requests to an API endpoint and
check the HTTP response and daemon state after the call. check the HTTP response and daemon state after the call.
@ -47,6 +47,24 @@ Bugs fixes should include a unit test case which exercises the bug.
A bug fix may also include new assertions in an existing integration tests for the A bug fix may also include new assertions in an existing integration tests for the
API endpoint. API endpoint.
### Integration tests environment considerations
When adding new tests or modifying existing test under `integration/`, testing
environment should be properly considered. `skip.If` from
[gotest.tools/skip](https://godoc.org/gotest.tools/skip) can be used to make the
test run conditionally. Full testing environment conditions can be found at
[environment.go](https://github.com/moby/moby/blob/cb37987ee11655ed6bbef663d245e55922354c68/internal/test/environment/environment.go)
Here is a quick example. If the test needs to interact with a docker daemon on
the same host, the following condition should be checked within the test code
```go
skip.If(t, testEnv.IsRemoteDaemon())
// your integration test code
```
If a remote daemon is detected, the test will be skipped.
## Running tests ## Running tests
To run the unit test suite: To run the unit test suite:

View File

@ -3,7 +3,7 @@ package api // import "github.com/docker/docker/api"
// Common constants for daemon and client. // Common constants for daemon and client.
const ( const (
// DefaultVersion of Current REST API // DefaultVersion of Current REST API
DefaultVersion = "1.38" DefaultVersion = "1.40"
// NoBaseImageSpecifier is the symbol used by the FROM // NoBaseImageSpecifier is the symbol used by the FROM
// command to specify that no base image is to be used. // command to specify that no base image is to be used.

View File

@ -19,10 +19,10 @@ produces:
consumes: consumes:
- "application/json" - "application/json"
- "text/plain" - "text/plain"
basePath: "/v1.38" basePath: "/v1.40"
info: info:
title: "Docker Engine API" title: "Docker Engine API"
version: "1.38" version: "1.40"
x-logo: x-logo:
url: "https://docs.docker.com/images/logo-docker-main.png" url: "https://docs.docker.com/images/logo-docker-main.png"
description: | description: |
@ -49,8 +49,8 @@ info:
the URL is not supported by the daemon, a HTTP `400 Bad Request` error message the URL is not supported by the daemon, a HTTP `400 Bad Request` error message
is returned. is returned.
If you omit the version-prefix, the current version of the API (v1.38) is used. If you omit the version-prefix, the current version of the API (v1.40) is used.
For example, calling `/info` is the same as calling `/v1.38/info`. Using the For example, calling `/info` is the same as calling `/v1.40/info`. Using the
API without a version-prefix is deprecated and will be removed in a future release. API without a version-prefix is deprecated and will be removed in a future release.
Engine releases in the near future should support this version of the API, Engine releases in the near future should support this version of the API,
@ -210,6 +210,43 @@ definitions:
PathInContainer: "/dev/deviceName" PathInContainer: "/dev/deviceName"
CgroupPermissions: "mrw" CgroupPermissions: "mrw"
DeviceRequest:
type: "object"
description: "A request for devices to be sent to device drivers"
properties:
Driver:
type: "string"
example: "nvidia"
Count:
type: "integer"
example: -1
DeviceIDs:
type: "array"
items:
type: "string"
example:
- "0"
- "1"
- "GPU-fef8089b-4820-abfc-e83e-94318197576e"
Capabilities:
description: |
A list of capabilities; an OR list of AND lists of capabilities.
type: "array"
items:
type: "array"
items:
type: "string"
example:
# gpu AND nvidia AND compute
- ["gpu", "nvidia", "compute"]
Options:
description: |
Driver-specific options, specified as a key/value pairs. These options
are passed directly to the driver.
type: "object"
additionalProperties:
type: "string"
ThrottleDevice: ThrottleDevice:
type: "object" type: "object"
properties: properties:
@ -238,11 +275,13 @@ definitions:
- `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container.
- `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed.
- `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs.
- `npipe` Mounts a named pipe from the host into the container. Must exist prior to creating the container.
type: "string" type: "string"
enum: enum:
- "bind" - "bind"
- "volume" - "volume"
- "tmpfs" - "tmpfs"
- "npipe"
ReadOnly: ReadOnly:
description: "Whether the mount should be read-only." description: "Whether the mount should be read-only."
type: "boolean" type: "boolean"
@ -263,6 +302,10 @@ definitions:
- "rshared" - "rshared"
- "slave" - "slave"
- "rslave" - "rslave"
NonRecursive:
description: "Disable recursive bind mount."
type: "boolean"
default: false
VolumeOptions: VolumeOptions:
description: "Optional configuration for the `volume` type." description: "Optional configuration for the `volume` type."
type: "object" type: "object"
@ -415,6 +458,11 @@ definitions:
items: items:
type: "string" type: "string"
example: "c 13:* rwm" example: "c 13:* rwm"
DeviceRequests:
description: "a list of requests for devices to be sent to device drivers"
type: "array"
items:
$ref: "#/definitions/DeviceRequest"
DiskQuota: DiskQuota:
description: "Disk limit (in bytes)." description: "Disk limit (in bytes)."
type: "integer" type: "integer"
@ -423,6 +471,11 @@ definitions:
description: "Kernel memory limit in bytes." description: "Kernel memory limit in bytes."
type: "integer" type: "integer"
format: "int64" format: "int64"
example: 209715200
KernelMemoryTCP:
description: "Hard limit for kernel TCP buffer memory (in bytes)."
type: "integer"
format: "int64"
MemoryReservation: MemoryReservation:
description: "Memory soft limit in bytes." description: "Memory soft limit in bytes."
type: "integer" type: "integer"
@ -449,9 +502,11 @@ definitions:
type: "boolean" type: "boolean"
x-nullable: true x-nullable: true
PidsLimit: PidsLimit:
description: "Tune a container's pids limit. Set -1 for unlimited." description: |
Tune a container's PIDs limit. Set `0` or `-1` for unlimited, or `null` to not change.
type: "integer" type: "integer"
format: "int64" format: "int64"
x-nullable: true
Ulimits: Ulimits:
description: | description: |
A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`"
@ -634,14 +689,22 @@ definitions:
$ref: "#/definitions/Mount" $ref: "#/definitions/Mount"
# Applicable to UNIX platforms # Applicable to UNIX platforms
Capabilities:
type: "array"
description: |
A list of kernel capabilities to be available for container (this overrides the default set).
Conflicts with options 'CapAdd' and 'CapDrop'"
items:
type: "string"
CapAdd: CapAdd:
type: "array" type: "array"
description: "A list of kernel capabilities to add to the container." description: "A list of kernel capabilities to add to the container. Conflicts with option 'Capabilities'"
items: items:
type: "string" type: "string"
CapDrop: CapDrop:
type: "array" type: "array"
description: "A list of kernel capabilities to drop from the container." description: "A list of kernel capabilities to drop from the container. Conflicts with option 'Capabilities'"
items: items:
type: "string" type: "string"
Dns: Dns:
@ -1473,11 +1536,9 @@ definitions:
type: "string" type: "string"
Options: Options:
description: "Driver-specific options, specified as a map." description: "Driver-specific options, specified as a map."
type: "array" type: "object"
items: additionalProperties:
type: "object" type: "string"
additionalProperties:
type: "string"
NetworkContainer: NetworkContainer:
type: "object" type: "object"
@ -1513,6 +1574,31 @@ definitions:
aux: aux:
$ref: "#/definitions/ImageID" $ref: "#/definitions/ImageID"
BuildCache:
type: "object"
properties:
ID:
type: "string"
Parent:
type: "string"
Type:
type: "string"
Description:
type: "string"
InUse:
type: "boolean"
Shared:
type: "boolean"
Size:
type: "integer"
CreatedAt:
type: "integer"
LastUsedAt:
type: "integer"
x-nullable: true
UsageCount:
type: "integer"
ImageID: ImageID:
type: "object" type: "object"
description: "Image ID or Digest" description: "Image ID or Digest"
@ -2434,6 +2520,31 @@ definitions:
description: "Whether there is currently a root CA rotation in progress for the swarm" description: "Whether there is currently a root CA rotation in progress for the swarm"
type: "boolean" type: "boolean"
example: false example: false
DataPathPort:
description: |
DataPathPort specifies the data path port number for data traffic.
Acceptable port range is 1024 to 49151.
If no port is set or is set to 0, the default port (4789) is used.
type: "integer"
format: "uint32"
default: 4789
example: 4789
DefaultAddrPool:
description: |
Default Address Pool specifies default subnet pools for global scope networks.
type: "array"
items:
type: "string"
format: "CIDR"
example: ["10.10.0.0/16", "20.20.0.0/16"]
SubnetSize:
description: |
SubnetSize specifies the subnet size of the networks created from the default subnet pool
type: "integer"
format: "uint32"
maximum: 29
default: 24
example: 24
JoinTokens: JoinTokens:
description: | description: |
@ -2556,8 +2667,20 @@ definitions:
type: "object" type: "object"
description: "CredentialSpec for managed service account (Windows only)" description: "CredentialSpec for managed service account (Windows only)"
properties: properties:
Config:
type: "string"
example: "0bt9dmxjvjiqermk6xrop3ekq"
description: |
Load credential spec from a Swarm Config with the given ID.
The specified config must also be present in the Configs field with the Runtime property set.
<p><br /></p>
> **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, and `CredentialSpec.Config` are mutually exclusive.
File: File:
type: "string" type: "string"
example: "spec.json"
description: | description: |
Load credential spec from this file. The file is read by the daemon, and must be present in the Load credential spec from this file. The file is read by the daemon, and must be present in the
`CredentialSpecs` subdirectory in the docker data directory, which defaults to `CredentialSpecs` subdirectory in the docker data directory, which defaults to
@ -2567,7 +2690,7 @@ definitions:
<p><br /></p> <p><br /></p>
> **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, and `CredentialSpec.Config` are mutually exclusive.
Registry: Registry:
type: "string" type: "string"
description: | description: |
@ -2579,7 +2702,7 @@ definitions:
<p><br /></p> <p><br /></p>
> **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, and `CredentialSpec.Config` are mutually exclusive.
SELinuxContext: SELinuxContext:
type: "object" type: "object"
description: "SELinux labels of the container" description: "SELinux labels of the container"
@ -2690,7 +2813,12 @@ definitions:
type: "object" type: "object"
properties: properties:
File: File:
description: "File represents a specific target that is backed by a file." description: |
File represents a specific target that is backed by a file.
<p><br /><p>
> **Note**: `Configs.File` and `Configs.Runtime` are mutually exclusive
type: "object" type: "object"
properties: properties:
Name: Name:
@ -2706,6 +2834,14 @@ definitions:
description: "Mode represents the FileMode of the file." description: "Mode represents the FileMode of the file."
type: "integer" type: "integer"
format: "uint32" format: "uint32"
Runtime:
description: |
Runtime represents a target that is not mounted into the container but is used by the task
<p><br /><p>
> **Note**: `Configs.File` and `Configs.Runtime` are mutually exclusive
type: "object"
ConfigID: ConfigID:
description: "ConfigID represents the ID of the specific config that we're referencing." description: "ConfigID represents the ID of the specific config that we're referencing."
type: "string" type: "string"
@ -2725,6 +2861,18 @@ definitions:
description: "Run an init inside the container that forwards signals and reaps processes. This field is omitted if empty, and the default (as configured on the daemon) is used." description: "Run an init inside the container that forwards signals and reaps processes. This field is omitted if empty, and the default (as configured on the daemon) is used."
type: "boolean" type: "boolean"
x-nullable: true x-nullable: true
Sysctls:
description: |
Set kernel namedspaced parameters (sysctls) in the container.
The Sysctls option on services accepts the same sysctls as the
are supported on containers. Note that while the same sysctls are
supported, no guarantees or checks are made about their
suitability for a clustered environment, and it's up to the user
to determine whether a given sysctl will work properly in a
Service.
type: "object"
additionalProperties:
type: "string"
NetworkAttachmentSpec: NetworkAttachmentSpec:
description: | description: |
Read-only spec type for non-swarm containers attached to swarm overlay Read-only spec type for non-swarm containers attached to swarm overlay
@ -2805,6 +2953,11 @@ definitions:
SpreadDescriptor: "node.labels.datacenter" SpreadDescriptor: "node.labels.datacenter"
- Spread: - Spread:
SpreadDescriptor: "node.labels.rack" SpreadDescriptor: "node.labels.rack"
MaxReplicas:
description: "Maximum number of replicas for per node (default value is 0, which is unlimited)"
type: "integer"
format: "int64"
default: 0
Platforms: Platforms:
description: | description: |
Platforms stores all the platforms that the service's image can Platforms stores all the platforms that the service's image can
@ -3605,6 +3758,10 @@ definitions:
See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt)
type: "boolean" type: "boolean"
example: true example: true
PidsLimit:
description: "Indicates if the host kernel has PID limit support enabled."
type: "boolean"
example: true
OomKillDisable: OomKillDisable:
description: "Indicates if OOM killer disable is supported on the host." description: "Indicates if OOM killer disable is supported on the host."
type: "boolean" type: "boolean"
@ -3722,18 +3879,22 @@ definitions:
description: | description: |
HTTP-proxy configured for the daemon. This value is obtained from the HTTP-proxy configured for the daemon. This value is obtained from the
[`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. [`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable.
Credentials ([user info component](https://tools.ietf.org/html/rfc3986#section-3.2.1)) in the proxy URL
are masked in the API response.
Containers do not automatically inherit this configuration. Containers do not automatically inherit this configuration.
type: "string" type: "string"
example: "http://user:pass@proxy.corp.example.com:8080" example: "http://xxxxx:xxxxx@proxy.corp.example.com:8080"
HttpsProxy: HttpsProxy:
description: | description: |
HTTPS-proxy configured for the daemon. This value is obtained from the HTTPS-proxy configured for the daemon. This value is obtained from the
[`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. [`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable.
Credentials ([user info component](https://tools.ietf.org/html/rfc3986#section-3.2.1)) in the proxy URL
are masked in the API response.
Containers do not automatically inherit this configuration. Containers do not automatically inherit this configuration.
type: "string" type: "string"
example: "https://user:pass@proxy.corp.example.com:4443" example: "https://xxxxx:xxxxx@proxy.corp.example.com:4443"
NoProxy: NoProxy:
description: | description: |
Comma-separated list of domain extensions for which no proxy should be Comma-separated list of domain extensions for which no proxy should be
@ -3823,10 +3984,10 @@ definitions:
$ref: "#/definitions/Runtime" $ref: "#/definitions/Runtime"
default: default:
runc: runc:
path: "docker-runc" path: "runc"
example: example:
runc: runc:
path: "docker-runc" path: "runc"
runc-master: runc-master:
path: "/go/bin/runc" path: "/go/bin/runc"
custom: custom:
@ -3896,6 +4057,27 @@ definitions:
- "name=seccomp,profile=default" - "name=seccomp,profile=default"
- "name=selinux" - "name=selinux"
- "name=userns" - "name=userns"
ProductLicense:
description: |
Reports a summary of the product license on the daemon.
If a commercial license has been applied to the daemon, information
such as number of nodes, and expiration are included.
type: "string"
example: "Community Engine"
Warnings:
description: |
List of warnings / informational messages about missing features, or
issues related to the daemon configuration.
These messages can be printed by the client as information to the user.
type: "array"
items:
type: "string"
example:
- "WARNING: No memory limit support"
- "WARNING: bridge-nf-call-iptables is disabled"
- "WARNING: bridge-nf-call-ip6tables is disabled"
# PluginsInfo is a temp struct holding Plugins name # PluginsInfo is a temp struct holding Plugins name
@ -4516,7 +4698,7 @@ paths:
OomKillDisable: false OomKillDisable: false
OomScoreAdj: 500 OomScoreAdj: 500
PidMode: "" PidMode: ""
PidsLimit: -1 PidsLimit: 0
PortBindings: PortBindings:
22/tcp: 22/tcp:
- HostPort: "11022" - HostPort: "11022"
@ -5922,7 +6104,7 @@ paths:
headers: headers:
X-Docker-Container-Path-Stat: X-Docker-Container-Path-Stat:
type: "string" type: "string"
description: "TODO" description: "A base64 - encoded JSON object with some filesystem header information about the path"
400: 400:
description: "Bad parameter" description: "Bad parameter"
schema: schema:
@ -6319,6 +6501,11 @@ paths:
description: "Target build stage" description: "Target build stage"
type: "string" type: "string"
default: "" default: ""
- name: "outputs"
in: "query"
description: "BuildKit output configuration"
type: "string"
default: ""
responses: responses:
200: 200:
description: "no error" description: "no error"
@ -6337,6 +6524,29 @@ paths:
produces: produces:
- "application/json" - "application/json"
operationId: "BuildPrune" operationId: "BuildPrune"
parameters:
- name: "keep-storage"
in: "query"
description: "Amount of disk space in bytes to keep for cache"
type: "integer"
format: "int64"
- name: "all"
in: "query"
type: "boolean"
description: "Remove all types of build cache"
- name: "filters"
in: "query"
type: "string"
description: |
A JSON encoded value of the filters (a `map[string][]string`) to process on the list of build cache objects. Available filters:
- `until=<duration>`: duration relative to daemon's time, during which build cache was not used, in Go's duration format (e.g., '24h')
- `id=<id>`
- `parent=<id>`
- `type=<string>`
- `description=<string>`
- `inuse`
- `shared`
- `private`
responses: responses:
200: 200:
description: "No error" description: "No error"
@ -6344,6 +6554,11 @@ paths:
type: "object" type: "object"
title: "BuildPruneResponse" title: "BuildPruneResponse"
properties: properties:
CachesDeleted:
type: "array"
items:
description: "ID of build cache object"
type: "string"
SpaceReclaimed: SpaceReclaimed:
description: "Disk space reclaimed in bytes" description: "Disk space reclaimed in bytes"
type: "integer" type: "integer"
@ -6972,9 +7187,57 @@ paths:
API-Version: API-Version:
type: "string" type: "string"
description: "Max API Version the server supports" description: "Max API Version the server supports"
BuildKit-Version:
type: "string"
description: "Default version of docker image builder"
Docker-Experimental: Docker-Experimental:
type: "boolean" type: "boolean"
description: "If the server is running with experimental mode enabled" description: "If the server is running with experimental mode enabled"
Cache-Control:
type: "string"
default: "no-cache, no-store, must-revalidate"
Pragma:
type: "string"
default: "no-cache"
500:
description: "server error"
schema:
$ref: "#/definitions/ErrorResponse"
headers:
Cache-Control:
type: "string"
default: "no-cache, no-store, must-revalidate"
Pragma:
type: "string"
default: "no-cache"
tags: ["System"]
head:
summary: "Ping"
description: "This is a dummy endpoint you can use to test if the server is accessible."
operationId: "SystemPingHead"
produces: ["text/plain"]
responses:
200:
description: "no error"
schema:
type: "string"
example: "(empty)"
headers:
API-Version:
type: "string"
description: "Max API Version the server supports"
BuildKit-Version:
type: "string"
description: "Default version of docker image builder"
Docker-Experimental:
type: "boolean"
description: "If the server is running with experimental mode enabled"
Cache-Control:
type: "string"
default: "no-cache, no-store, must-revalidate"
Pragma:
type: "string"
default: "no-cache"
500: 500:
description: "server error" description: "server error"
schema: schema:
@ -7175,6 +7438,10 @@ paths:
type: "array" type: "array"
items: items:
$ref: "#/definitions/Volume" $ref: "#/definitions/Volume"
BuildCache:
type: "array"
items:
$ref: "#/definitions/BuildCache"
example: example:
LayersSize: 1092588 LayersSize: 1092588
Images: Images:
@ -7589,6 +7856,7 @@ paths:
schema: schema:
type: "object" type: "object"
title: "VolumeListResponse" title: "VolumeListResponse"
description: "Volume list response"
required: [Volumes, Warnings] required: [Volumes, Warnings]
properties: properties:
Volumes: Volumes:
@ -7665,6 +7933,8 @@ paths:
description: "Volume configuration" description: "Volume configuration"
schema: schema:
type: "object" type: "object"
description: "Volume configuration"
title: "VolumeConfig"
properties: properties:
Name: Name:
description: "The new volume's name. If not specified, Docker generates a name." description: "The new volume's name. If not specified, Docker generates a name."
@ -7865,6 +8135,10 @@ paths:
description: | description: |
JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters:
- `dangling=<boolean>` When set to `true` (or `1`), returns all
networks that are not in use by a container. When set to `false`
(or `0`), only networks that are in use by one or more
containers are returned.
- `driver=<driver-name>` Matches a network's driver. - `driver=<driver-name>` Matches a network's driver.
- `id=<network-id>` Matches all or part of a network ID. - `id=<network-id>` Matches all or part of a network ID.
- `label=<key>` or `label=<key>=<value>` of a network label. - `label=<key>` or `label=<key>=<value>` of a network label.
@ -8582,6 +8856,7 @@ paths:
- `label=<engine label>` - `label=<engine label>`
- `membership=`(`accepted`|`pending`)` - `membership=`(`accepted`|`pending`)`
- `name=<node name>` - `name=<node name>`
- `node.label=<node label>`
- `role=`(`manager`|`worker`)` - `role=`(`manager`|`worker`)`
type: "string" type: "string"
tags: ["Node"] tags: ["Node"]
@ -8754,14 +9029,36 @@ paths:
nodes in order to reach the containers running on this node. Using this parameter it is possible to nodes in order to reach the containers running on this node. Using this parameter it is possible to
separate the container data traffic from the management traffic of the cluster. separate the container data traffic from the management traffic of the cluster.
type: "string" type: "string"
DataPathPort:
description: |
DataPathPort specifies the data path port number for data traffic.
Acceptable port range is 1024 to 49151.
if no port is set or is set to 0, default port 4789 will be used.
type: "integer"
format: "uint32"
DefaultAddrPool:
description: |
Default Address Pool specifies default subnet pools for global scope networks.
type: "array"
items:
type: "string"
example: ["10.10.0.0/16", "20.20.0.0/16"]
ForceNewCluster: ForceNewCluster:
description: "Force creation of a new swarm." description: "Force creation of a new swarm."
type: "boolean" type: "boolean"
SubnetSize:
description: |
SubnetSize specifies the subnet size of the networks created from the default subnet pool
type: "integer"
format: "uint32"
Spec: Spec:
$ref: "#/definitions/SwarmSpec" $ref: "#/definitions/SwarmSpec"
example: example:
ListenAddr: "0.0.0.0:2377" ListenAddr: "0.0.0.0:2377"
AdvertiseAddr: "192.168.1.1:2377" AdvertiseAddr: "192.168.1.1:2377"
DataPathPort: 4789
DefaultAddrPool: ["10.10.0.0/8", "20.20.0.0/8"]
SubnetSize: 24
ForceNewCluster: false ForceNewCluster: false
Spec: Spec:
Orchestration: {} Orchestration: {}
@ -9243,7 +9540,10 @@ paths:
- name: "version" - name: "version"
in: "query" in: "query"
description: "The version number of the service object being updated. This is required to avoid conflicting writes." description: "The version number of the service object being updated.
This is required to avoid conflicting writes.
This version number should be the value as currently set on the service *before* the update.
You can find the current version by calling `GET /services/{id}`"
required: true required: true
type: "integer" type: "integer"
- name: "registryAuthFrom" - name: "registryAuthFrom"
@ -9602,6 +9902,7 @@ paths:
description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines."
type: "string" type: "string"
default: "all" default: "all"
tags: ["Task"]
/secrets: /secrets:
get: get:
summary: "List secrets" summary: "List secrets"

View File

@ -7,7 +7,7 @@ import (
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/go-units" units "github.com/docker/go-units"
) )
// CheckpointCreateOptions holds parameters to create a checkpoint from a container // CheckpointCreateOptions holds parameters to create a checkpoint from a container
@ -181,8 +181,33 @@ type ImageBuildOptions struct {
Target string Target string
SessionID string SessionID string
Platform string Platform string
// Version specifies the version of the unerlying builder to use
Version BuilderVersion
// BuildID is an optional identifier that can be passed together with the
// build request. The same identifier can be used to gracefully cancel the
// build with the cancel request.
BuildID string
// Outputs defines configurations for exporting build results. Only supported
// in BuildKit mode
Outputs []ImageBuildOutput
} }
// ImageBuildOutput defines configuration for exporting a build result
type ImageBuildOutput struct {
Type string
Attrs map[string]string
}
// BuilderVersion sets the version of underlying builder to use
type BuilderVersion string
const (
// BuilderV1 is the first generation builder in docker daemon
BuilderV1 BuilderVersion = "1"
// BuilderBuildKit is builder based on moby/buildkit project
BuilderBuildKit = "2"
)
// ImageBuildResponse holds information // ImageBuildResponse holds information
// returned by a server after building // returned by a server after building
// an image. // an image.

View File

@ -55,3 +55,10 @@ type PluginEnableConfig struct {
type PluginDisableConfig struct { type PluginDisableConfig struct {
ForceDisable bool ForceDisable bool
} }
// NetworkListConfig stores the options available for listing networks
type NetworkListConfig struct {
// TODO(@cpuguy83): naming is hard, this is pulled from what was being used in the router before moving here
Detailed bool
Verbose bool
}

View File

@ -54,7 +54,7 @@ type Config struct {
Env []string // List of environment variable to set in the container Env []string // List of environment variable to set in the container
Cmd strslice.StrSlice // Command to run when starting the container Cmd strslice.StrSlice // Command to run when starting the container
Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (Windows specific) ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (meaning treat as a command line) (Windows specific).
Image string // Name of the image as it was passed by the operator (e.g. could be symbolic) Image string // Name of the image as it was passed by the operator (e.g. could be symbolic)
Volumes map[string]struct{} // List of volumes (mounts) used for the container Volumes map[string]struct{} // List of volumes (mounts) used for the container
WorkingDir string // Current directory (PWD) in the command will be launched WorkingDir string // Current directory (PWD) in the command will be launched

View File

@ -244,6 +244,16 @@ func (n PidMode) Container() string {
return "" return ""
} }
// DeviceRequest represents a request for devices from a device driver.
// Used by GPU device drivers.
type DeviceRequest struct {
Driver string // Name of device driver
Count int // Number of devices to request (-1 = All)
DeviceIDs []string // List of device IDs as recognizable by the device driver
Capabilities [][]string // An OR list of AND lists of device capabilities (e.g. "gpu")
Options map[string]string // Options to pass onto the device driver
}
// DeviceMapping represents the device mapping between the host and the container. // DeviceMapping represents the device mapping between the host and the container.
type DeviceMapping struct { type DeviceMapping struct {
PathOnHost string PathOnHost string
@ -327,13 +337,15 @@ type Resources struct {
CpusetMems string // CpusetMems 0-2, 0,1 CpusetMems string // CpusetMems 0-2, 0,1
Devices []DeviceMapping // List of devices to map inside the container Devices []DeviceMapping // List of devices to map inside the container
DeviceCgroupRules []string // List of rule to be added to the device cgroup DeviceCgroupRules []string // List of rule to be added to the device cgroup
DeviceRequests []DeviceRequest // List of device requests for device drivers
DiskQuota int64 // Disk limit (in bytes) DiskQuota int64 // Disk limit (in bytes)
KernelMemory int64 // Kernel memory limit (in bytes) KernelMemory int64 // Kernel memory limit (in bytes)
KernelMemoryTCP int64 // Hard limit for kernel TCP buffer memory (in bytes)
MemoryReservation int64 // Memory soft limit (in bytes) MemoryReservation int64 // Memory soft limit (in bytes)
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
MemorySwappiness *int64 // Tuning container memory swappiness behaviour MemorySwappiness *int64 // Tuning container memory swappiness behaviour
OomKillDisable *bool // Whether to disable OOM Killer or not OomKillDisable *bool // Whether to disable OOM Killer or not
PidsLimit int64 // Setting pids limit for a container PidsLimit *int64 // Setting PIDs limit for a container; Set `0` or `-1` for unlimited, or `null` to not change.
Ulimits []*units.Ulimit // List of ulimits to be set in the container Ulimits []*units.Ulimit // List of ulimits to be set in the container
// Applicable to Windows // Applicable to Windows
@ -369,9 +381,10 @@ type HostConfig struct {
// Applicable to UNIX platforms // Applicable to UNIX platforms
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container
DNS []string `json:"Dns"` // List of DNS server to lookup Capabilities []string `json:"Capabilities"` // List of kernel capabilities to be available for container (this overrides the default set)
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for DNS []string `json:"Dns"` // List of DNS server to lookup
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
ExtraHosts []string // List of extra hosts ExtraHosts []string // List of extra hosts
GroupAdd []string // List of additional groups that the container process will run as GroupAdd []string // List of additional groups that the container process will run as
IpcMode IpcMode // IPC namespace to use for the container IpcMode IpcMode // IPC namespace to use for the container

View File

@ -323,6 +323,22 @@ func (args Args) WalkValues(field string, op func(value string) error) error {
return nil return nil
} }
// Clone returns a copy of args.
func (args Args) Clone() (newArgs Args) {
newArgs.fields = make(map[string]map[string]bool, len(args.fields))
for k, m := range args.fields {
var mm map[string]bool
if m != nil {
mm = make(map[string]bool, len(m))
for kk, v := range m {
mm[kk] = v
}
}
newArgs.fields[k] = mm
}
return newArgs
}
func deprecatedArgs(d map[string][]string) map[string]map[string]bool { func deprecatedArgs(d map[string][]string) map[string]map[string]bool {
m := map[string]map[string]bool{} m := map[string]map[string]bool{}
for k, v := range d { for k, v := range d {

View File

@ -79,7 +79,8 @@ const (
// BindOptions defines options specific to mounts of type "bind". // BindOptions defines options specific to mounts of type "bind".
type BindOptions struct { type BindOptions struct {
Propagation Propagation `json:",omitempty"` Propagation Propagation `json:",omitempty"`
NonRecursive bool `json:",omitempty"`
} }
// VolumeOptions represents the options for a mount of type volume. // VolumeOptions represents the options for a mount of type volume.

View File

@ -1,4 +1,8 @@
package network // import "github.com/docker/docker/api/types/network" package network // import "github.com/docker/docker/api/types/network"
import (
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/errdefs"
)
// Address represents an IP address // Address represents an IP address
type Address struct { type Address struct {
@ -106,3 +110,18 @@ type NetworkingConfig struct {
type ConfigReference struct { type ConfigReference struct {
Network string Network string
} }
var acceptedFilters = map[string]bool{
"dangling": true,
"driver": true,
"id": true,
"label": true,
"name": true,
"scope": true,
"type": true,
}
// ValidateFilters validates the list of filter args with the available filters.
func ValidateFilters(filter filters.Args) error {
return errdefs.InvalidParameter(filter.Validate(acceptedFilters))
}

View File

@ -77,8 +77,9 @@ type Arg struct {
// Filter is used to conditionally apply Seccomp rules // Filter is used to conditionally apply Seccomp rules
type Filter struct { type Filter struct {
Caps []string `json:"caps,omitempty"` Caps []string `json:"caps,omitempty"`
Arches []string `json:"arches,omitempty"` Arches []string `json:"arches,omitempty"`
MinKernel string `json:"minKernel,omitempty"`
} }
// Syscall is used to match a group of syscalls in Seccomp // Syscall is used to match a group of syscalls in Seccomp

View File

@ -120,7 +120,7 @@ type NetworkStats struct {
RxBytes uint64 `json:"rx_bytes"` RxBytes uint64 `json:"rx_bytes"`
// Packets received. Windows and Linux. // Packets received. Windows and Linux.
RxPackets uint64 `json:"rx_packets"` RxPackets uint64 `json:"rx_packets"`
// Received errors. Not used on Windows. Note that we dont `omitempty` this // Received errors. Not used on Windows. Note that we don't `omitempty` this
// field as it is expected in the >=v1.21 API stats structure. // field as it is expected in the >=v1.21 API stats structure.
RxErrors uint64 `json:"rx_errors"` RxErrors uint64 `json:"rx_errors"`
// Incoming packets dropped. Windows and Linux. // Incoming packets dropped. Windows and Linux.
@ -129,7 +129,7 @@ type NetworkStats struct {
TxBytes uint64 `json:"tx_bytes"` TxBytes uint64 `json:"tx_bytes"`
// Packets sent. Windows and Linux. // Packets sent. Windows and Linux.
TxPackets uint64 `json:"tx_packets"` TxPackets uint64 `json:"tx_packets"`
// Sent errors. Not used on Windows. Note that we dont `omitempty` this // Sent errors. Not used on Windows. Note that we don't `omitempty` this
// field as it is expected in the >=v1.21 API stats structure. // field as it is expected in the >=v1.21 API stats structure.
TxErrors uint64 `json:"tx_errors"` TxErrors uint64 `json:"tx_errors"`
// Outgoing packets dropped. Windows and Linux. // Outgoing packets dropped. Windows and Linux.

View File

@ -27,9 +27,14 @@ type ConfigReferenceFileTarget struct {
Mode os.FileMode Mode os.FileMode
} }
// ConfigReferenceRuntimeTarget is a target for a config specifying that it
// isn't mounted into the container but instead has some other purpose.
type ConfigReferenceRuntimeTarget struct{}
// ConfigReference is a reference to a config in swarm // ConfigReference is a reference to a config in swarm
type ConfigReference struct { type ConfigReference struct {
File *ConfigReferenceFileTarget File *ConfigReferenceFileTarget `json:",omitempty"`
Runtime *ConfigReferenceRuntimeTarget `json:",omitempty"`
ConfigID string ConfigID string
ConfigName string ConfigName string
} }

View File

@ -33,6 +33,7 @@ type SELinuxContext struct {
// CredentialSpec for managed service account (Windows only) // CredentialSpec for managed service account (Windows only)
type CredentialSpec struct { type CredentialSpec struct {
Config string
File string File string
Registry string Registry string
} }
@ -71,4 +72,5 @@ type ContainerSpec struct {
Secrets []*SecretReference `json:",omitempty"` Secrets []*SecretReference `json:",omitempty"`
Configs []*ConfigReference `json:",omitempty"` Configs []*ConfigReference `json:",omitempty"`
Isolation container.Isolation `json:",omitempty"` Isolation container.Isolation `json:",omitempty"`
Sysctls map[string]string `json:",omitempty"`
} }

View File

@ -1,6 +1,8 @@
package swarm // import "github.com/docker/docker/api/types/swarm" package swarm // import "github.com/docker/docker/api/types/swarm"
import "time" import (
"time"
)
// ClusterInfo represents info about the cluster for outputting in "info" // ClusterInfo represents info about the cluster for outputting in "info"
// it contains the same information as "Swarm", but without the JoinTokens // it contains the same information as "Swarm", but without the JoinTokens
@ -10,6 +12,9 @@ type ClusterInfo struct {
Spec Spec Spec Spec
TLSInfo TLSInfo TLSInfo TLSInfo
RootRotationInProgress bool RootRotationInProgress bool
DefaultAddrPool []string
SubnetSize uint32
DataPathPort uint32
} }
// Swarm represents a swarm. // Swarm represents a swarm.
@ -149,10 +154,13 @@ type InitRequest struct {
ListenAddr string ListenAddr string
AdvertiseAddr string AdvertiseAddr string
DataPathAddr string DataPathAddr string
DataPathPort uint32
ForceNewCluster bool ForceNewCluster bool
Spec Spec Spec Spec
AutoLockManagers bool AutoLockManagers bool
Availability NodeAvailability Availability NodeAvailability
DefaultAddrPool []string
SubnetSize uint32
} }
// JoinRequest is the request used to join a swarm. // JoinRequest is the request used to join a swarm.
@ -201,6 +209,8 @@ type Info struct {
Managers int `json:",omitempty"` Managers int `json:",omitempty"`
Cluster *ClusterInfo `json:",omitempty"` Cluster *ClusterInfo `json:",omitempty"`
Warnings []string `json:",omitempty"`
} }
// Peer represents a peer. // Peer represents a peer.

View File

@ -127,6 +127,7 @@ type ResourceRequirements struct {
type Placement struct { type Placement struct {
Constraints []string `json:",omitempty"` Constraints []string `json:",omitempty"`
Preferences []PlacementPreference `json:",omitempty"` Preferences []PlacementPreference `json:",omitempty"`
MaxReplicas uint64 `json:",omitempty"`
// Platforms stores all the platforms that the image can run on. // Platforms stores all the platforms that the image can run on.
// This field is used in the platform filter for scheduling. If empty, // This field is used in the platform filter for scheduling. If empty,

View File

@ -102,9 +102,10 @@ type ContainerStats struct {
// Ping contains response of Engine API: // Ping contains response of Engine API:
// GET "/_ping" // GET "/_ping"
type Ping struct { type Ping struct {
APIVersion string APIVersion string
OSType string OSType string
Experimental bool Experimental bool
BuilderVersion BuilderVersion
} }
// ComponentVersion describes the version information for a specific component. // ComponentVersion describes the version information for a specific component.
@ -157,10 +158,12 @@ type Info struct {
MemoryLimit bool MemoryLimit bool
SwapLimit bool SwapLimit bool
KernelMemory bool KernelMemory bool
KernelMemoryTCP bool
CPUCfsPeriod bool `json:"CpuCfsPeriod"` CPUCfsPeriod bool `json:"CpuCfsPeriod"`
CPUCfsQuota bool `json:"CpuCfsQuota"` CPUCfsQuota bool `json:"CpuCfsQuota"`
CPUShares bool CPUShares bool
CPUSet bool CPUSet bool
PidsLimit bool
IPv4Forwarding bool IPv4Forwarding bool
BridgeNfIptables bool BridgeNfIptables bool
BridgeNfIP6tables bool `json:"BridgeNfIp6tables"` BridgeNfIP6tables bool `json:"BridgeNfIp6tables"`
@ -204,6 +207,8 @@ type Info struct {
RuncCommit Commit RuncCommit Commit
InitCommit Commit InitCommit Commit
SecurityOptions []string SecurityOptions []string
ProductLicense string `json:",omitempty"`
Warnings []string
} }
// KeyValue holds a key/value pair // KeyValue holds a key/value pair
@ -512,7 +517,8 @@ type DiskUsage struct {
Images []*ImageSummary Images []*ImageSummary
Containers []*Container Containers []*Container
Volumes []*Volume Volumes []*Volume
BuilderSize int64 BuildCache []*BuildCache
BuilderSize int64 // deprecated
} }
// ContainersPruneReport contains the response for Engine API: // ContainersPruneReport contains the response for Engine API:
@ -539,6 +545,7 @@ type ImagesPruneReport struct {
// BuildCachePruneReport contains the response for Engine API: // BuildCachePruneReport contains the response for Engine API:
// POST "/build/prune" // POST "/build/prune"
type BuildCachePruneReport struct { type BuildCachePruneReport struct {
CachesDeleted []string
SpaceReclaimed uint64 SpaceReclaimed uint64
} }
@ -585,3 +592,24 @@ type PushResult struct {
type BuildResult struct { type BuildResult struct {
ID string ID string
} }
// BuildCache contains information about a build cache record
type BuildCache struct {
ID string
Parent string
Type string
Description string
InUse bool
Shared bool
Size int64
CreatedAt time.Time
LastUsedAt *time.Time
UsageCount int
}
// BuildCachePruneOptions hold parameters to prune the build cache
type BuildCachePruneOptions struct {
All bool
KeepStorage int64
Filters filters.Args
}

View File

@ -7,7 +7,7 @@ package volume
// See hack/generate-swagger-api.sh // See hack/generate-swagger-api.sh
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// VolumeCreateBody // VolumeCreateBody Volume configuration
// swagger:model VolumeCreateBody // swagger:model VolumeCreateBody
type VolumeCreateBody struct { type VolumeCreateBody struct {

View File

@ -9,7 +9,7 @@ package volume
import "github.com/docker/docker/api/types" import "github.com/docker/docker/api/types"
// VolumeListOKBody // VolumeListOKBody Volume list response
// swagger:model VolumeListOKBody // swagger:model VolumeListOKBody
type VolumeListOKBody struct { type VolumeListOKBody struct {

View File

@ -16,7 +16,7 @@ import (
) )
func main() { func main() {
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
panic(err) panic(err)
} }

16
vendor/github.com/docker/docker/client/build_cancel.go generated vendored Normal file
View File

@ -0,0 +1,16 @@
package client // import "github.com/docker/docker/client"
import (
"context"
"net/url"
)
// BuildCancel requests the daemon to cancel ongoing build request
func (cli *Client) BuildCancel(ctx context.Context, id string) error {
query := url.Values{}
query.Set("id", id)
serverResp, err := cli.post(ctx, "/build/cancel", query, nil, nil)
ensureReaderClosed(serverResp)
return err
}

View File

@ -4,23 +4,38 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/pkg/errors"
) )
// BuildCachePrune requests the daemon to delete unused cache data // BuildCachePrune requests the daemon to delete unused cache data
func (cli *Client) BuildCachePrune(ctx context.Context) (*types.BuildCachePruneReport, error) { func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) {
if err := cli.NewVersionError("1.31", "build prune"); err != nil { if err := cli.NewVersionError("1.31", "build prune"); err != nil {
return nil, err return nil, err
} }
report := types.BuildCachePruneReport{} report := types.BuildCachePruneReport{}
serverResp, err := cli.post(ctx, "/build/prune", nil, nil, nil) query := url.Values{}
if opts.All {
query.Set("all", "1")
}
query.Set("keep-storage", fmt.Sprintf("%d", opts.KeepStorage))
filters, err := filters.ToJSON(opts.Filters)
if err != nil {
return nil, errors.Wrap(err, "prune could not marshal filters option")
}
query.Set("filters", filters)
serverResp, err := cli.post(ctx, "/build/prune", query, nil, nil)
defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer ensureReaderClosed(serverResp)
if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil {
return nil, fmt.Errorf("Error retrieving disk usage: %v", err) return nil, fmt.Errorf("Error retrieving disk usage: %v", err)

View File

@ -18,11 +18,11 @@ func (cli *Client) CheckpointList(ctx context.Context, container string, options
} }
resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil) resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil)
defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return checkpoints, wrapResponseError(err, resp, "container", container) return checkpoints, wrapResponseError(err, resp, "container", container)
} }
err = json.NewDecoder(resp.body).Decode(&checkpoints) err = json.NewDecoder(resp.body).Decode(&checkpoints)
ensureReaderClosed(resp)
return checkpoints, err return checkpoints, err
} }

View File

@ -23,7 +23,7 @@ For example, to list running containers (the equivalent of "docker ps"):
) )
func main() { func main() {
cli, err := client.NewEnvClient() cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -47,16 +47,13 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"os"
"path" "path"
"path/filepath"
"strings" "strings"
"github.com/docker/docker/api" "github.com/docker/docker/api"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions"
"github.com/docker/go-connections/sockets" "github.com/docker/go-connections/sockets"
"github.com/docker/go-connections/tlsconfig"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -90,7 +87,7 @@ type Client struct {
// If the request is non-GET return `ErrRedirect`. Otherwise use the last response. // If the request is non-GET return `ErrRedirect`. Otherwise use the last response.
// //
// Go 1.8 changes behavior for HTTP redirects (specifically 301, 307, and 308) in the client . // Go 1.8 changes behavior for HTTP redirects (specifically 301, 307, and 308) in the client .
// The Docker client (and by extension docker API client) can be made to to send a request // The Docker client (and by extension docker API client) can be made to send a request
// like POST /containers//start where what would normally be in the name section of the URL is empty. // like POST /containers//start where what would normally be in the name section of the URL is empty.
// This triggers an HTTP 301 from the daemon. // This triggers an HTTP 301 from the daemon.
// In go 1.8 this 301 will be converted to a GET request, and ends up getting a 404 from the daemon. // In go 1.8 this 301 will be converted to a GET request, and ends up getting a 404 from the daemon.
@ -103,130 +100,6 @@ func CheckRedirect(req *http.Request, via []*http.Request) error {
return ErrRedirect return ErrRedirect
} }
// NewEnvClient initializes a new API client based on environment variables.
// See FromEnv for a list of support environment variables.
//
// Deprecated: use NewClientWithOpts(FromEnv)
func NewEnvClient() (*Client, error) {
return NewClientWithOpts(FromEnv)
}
// FromEnv configures the client with values from environment variables.
//
// Supported environment variables:
// DOCKER_HOST to set the url to the docker server.
// DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest.
// DOCKER_CERT_PATH to load the TLS certificates from.
// DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default.
func FromEnv(c *Client) error {
if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" {
options := tlsconfig.Options{
CAFile: filepath.Join(dockerCertPath, "ca.pem"),
CertFile: filepath.Join(dockerCertPath, "cert.pem"),
KeyFile: filepath.Join(dockerCertPath, "key.pem"),
InsecureSkipVerify: os.Getenv("DOCKER_TLS_VERIFY") == "",
}
tlsc, err := tlsconfig.Client(options)
if err != nil {
return err
}
c.client = &http.Client{
Transport: &http.Transport{TLSClientConfig: tlsc},
CheckRedirect: CheckRedirect,
}
}
if host := os.Getenv("DOCKER_HOST"); host != "" {
if err := WithHost(host)(c); err != nil {
return err
}
}
if version := os.Getenv("DOCKER_API_VERSION"); version != "" {
c.version = version
c.manualOverride = true
}
return nil
}
// WithTLSClientConfig applies a tls config to the client transport.
func WithTLSClientConfig(cacertPath, certPath, keyPath string) func(*Client) error {
return func(c *Client) error {
opts := tlsconfig.Options{
CAFile: cacertPath,
CertFile: certPath,
KeyFile: keyPath,
ExclusiveRootPools: true,
}
config, err := tlsconfig.Client(opts)
if err != nil {
return errors.Wrap(err, "failed to create tls config")
}
if transport, ok := c.client.Transport.(*http.Transport); ok {
transport.TLSClientConfig = config
return nil
}
return errors.Errorf("cannot apply tls config to transport: %T", c.client.Transport)
}
}
// WithDialer applies the dialer.DialContext to the client transport. This can be
// used to set the Timeout and KeepAlive settings of the client.
func WithDialer(dialer *net.Dialer) func(*Client) error {
return func(c *Client) error {
if transport, ok := c.client.Transport.(*http.Transport); ok {
transport.DialContext = dialer.DialContext
return nil
}
return errors.Errorf("cannot apply dialer to transport: %T", c.client.Transport)
}
}
// WithVersion overrides the client version with the specified one
func WithVersion(version string) func(*Client) error {
return func(c *Client) error {
c.version = version
return nil
}
}
// WithHost overrides the client host with the specified one.
func WithHost(host string) func(*Client) error {
return func(c *Client) error {
hostURL, err := ParseHostURL(host)
if err != nil {
return err
}
c.host = host
c.proto = hostURL.Scheme
c.addr = hostURL.Host
c.basePath = hostURL.Path
if transport, ok := c.client.Transport.(*http.Transport); ok {
return sockets.ConfigureTransport(transport, c.proto, c.addr)
}
return errors.Errorf("cannot apply host to transport: %T", c.client.Transport)
}
}
// WithHTTPClient overrides the client http client with the specified one
func WithHTTPClient(client *http.Client) func(*Client) error {
return func(c *Client) error {
if client != nil {
c.client = client
}
return nil
}
}
// WithHTTPHeaders overrides the client default http headers
func WithHTTPHeaders(headers map[string]string) func(*Client) error {
return func(c *Client) error {
c.customHTTPHeaders = headers
return nil
}
}
// NewClientWithOpts initializes a new API client with default values. It takes functors // NewClientWithOpts initializes a new API client with default values. It takes functors
// to modify values when creating it, like `NewClientWithOpts(WithVersion(…))` // to modify values when creating it, like `NewClientWithOpts(WithVersion(…))`
// It also initializes the custom http headers to add to each request. // It also initializes the custom http headers to add to each request.
@ -242,7 +115,6 @@ func NewClientWithOpts(ops ...func(*Client) error) (*Client, error) {
c := &Client{ c := &Client{
host: DefaultDockerHost, host: DefaultDockerHost,
version: api.DefaultVersion, version: api.DefaultVersion,
scheme: "http",
client: client, client: client,
proto: defaultProto, proto: defaultProto,
addr: defaultAddr, addr: defaultAddr,
@ -257,14 +129,18 @@ func NewClientWithOpts(ops ...func(*Client) error) (*Client, error) {
if _, ok := c.client.Transport.(http.RoundTripper); !ok { if _, ok := c.client.Transport.(http.RoundTripper); !ok {
return nil, fmt.Errorf("unable to verify TLS configuration, invalid transport %v", c.client.Transport) return nil, fmt.Errorf("unable to verify TLS configuration, invalid transport %v", c.client.Transport)
} }
tlsConfig := resolveTLSConfig(c.client.Transport) if c.scheme == "" {
if tlsConfig != nil { c.scheme = "http"
// TODO(stevvooe): This isn't really the right way to write clients in Go.
// `NewClient` should probably only take an `*http.Client` and work from there. tlsConfig := resolveTLSConfig(c.client.Transport)
// Unfortunately, the model of having a host-ish/url-thingy as the connection if tlsConfig != nil {
// string has us confusing protocol and transport layers. We continue doing // TODO(stevvooe): This isn't really the right way to write clients in Go.
// this to avoid breaking existing clients but this should be addressed. // `NewClient` should probably only take an `*http.Client` and work from there.
c.scheme = "https" // Unfortunately, the model of having a host-ish/url-thingy as the connection
// string has us confusing protocol and transport layers. We continue doing
// this to avoid breaking existing clients but this should be addressed.
c.scheme = "https"
}
} }
return c, nil return c, nil
@ -283,18 +159,6 @@ func defaultHTTPClient(host string) (*http.Client, error) {
}, nil }, nil
} }
// NewClient initializes a new API client for the given host and API version.
// It uses the given http client as transport.
// It also initializes the custom http headers to add to each request.
//
// It won't send any version information if the version number is empty. It is
// highly recommended that you set a version or your client may break if the
// server is upgraded.
// Deprecated: use NewClientWithOpts
func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) {
return NewClientWithOpts(WithHost(host), WithVersion(version), WithHTTPClient(client), WithHTTPHeaders(httpHeaders))
}
// Close the transport used by the client // Close the transport used by the client
func (cli *Client) Close() error { func (cli *Client) Close() error {
if t, ok := cli.client.Transport.(*http.Transport); ok { if t, ok := cli.client.Transport.(*http.Transport); ok {
@ -400,3 +264,16 @@ func (cli *Client) CustomHTTPHeaders() map[string]string {
func (cli *Client) SetCustomHTTPHeaders(headers map[string]string) { func (cli *Client) SetCustomHTTPHeaders(headers map[string]string) {
cli.customHTTPHeaders = headers cli.customHTTPHeaders = headers
} }
// Dialer returns a dialer for a raw stream connection, with HTTP/1.1 header, that can be used for proxying the daemon connection.
// Used by `docker dial-stdio` (docker/cli#889).
func (cli *Client) Dialer() func(context.Context) (net.Conn, error) {
return func(ctx context.Context) (net.Conn, error) {
if transport, ok := cli.client.Transport.(*http.Transport); ok {
if transport.DialContext != nil && transport.TLSClientConfig == nil {
return transport.DialContext(ctx, cli.proto, cli.addr)
}
}
return fallbackDial(cli.proto, cli.addr, resolveTLSConfig(cli.client.Transport))
}
}

View File

@ -0,0 +1,23 @@
package client
import "net/http"
// NewClient initializes a new API client for the given host and API version.
// It uses the given http client as transport.
// It also initializes the custom http headers to add to each request.
//
// It won't send any version information if the version number is empty. It is
// highly recommended that you set a version or your client may break if the
// server is upgraded.
// Deprecated: use NewClientWithOpts
func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) {
return NewClientWithOpts(WithHost(host), WithVersion(version), WithHTTPClient(client), WithHTTPHeaders(httpHeaders))
}
// NewEnvClient initializes a new API client based on environment variables.
// See FromEnv for a list of support environment variables.
//
// Deprecated: use NewClientWithOpts(FromEnv)
func NewEnvClient() (*Client, error) {
return NewClientWithOpts(FromEnv)
}

View File

@ -15,11 +15,11 @@ func (cli *Client) ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (t
return response, err return response, err
} }
resp, err := cli.post(ctx, "/configs/create", nil, config, nil) resp, err := cli.post(ctx, "/configs/create", nil, config, nil)
defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return response, err return response, err
} }
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.body).Decode(&response)
ensureReaderClosed(resp)
return response, err return response, err
} }

View File

@ -18,10 +18,10 @@ func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.C
return swarm.Config{}, nil, err return swarm.Config{}, nil, err
} }
resp, err := cli.get(ctx, "/configs/"+id, nil, nil) resp, err := cli.get(ctx, "/configs/"+id, nil, nil)
defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return swarm.Config{}, nil, wrapResponseError(err, resp, "config", id) return swarm.Config{}, nil, wrapResponseError(err, resp, "config", id)
} }
defer ensureReaderClosed(resp)
body, err := ioutil.ReadAll(resp.body) body, err := ioutil.ReadAll(resp.body)
if err != nil { if err != nil {

View File

@ -27,12 +27,12 @@ func (cli *Client) ConfigList(ctx context.Context, options types.ConfigListOptio
} }
resp, err := cli.get(ctx, "/configs", query, nil) resp, err := cli.get(ctx, "/configs", query, nil)
defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var configs []swarm.Config var configs []swarm.Config
err = json.NewDecoder(resp.body).Decode(&configs) err = json.NewDecoder(resp.body).Decode(&configs)
ensureReaderClosed(resp)
return configs, err return configs, err
} }

View File

@ -8,6 +8,6 @@ func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
return err return err
} }
resp, err := cli.delete(ctx, "/configs/"+id, nil, nil) resp, err := cli.delete(ctx, "/configs/"+id, nil, nil)
ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "config", id) return wrapResponseError(err, resp, "config", id)
} }

View File

@ -45,11 +45,11 @@ func (cli *Client) ContainerCommit(ctx context.Context, container string, option
var response types.IDResponse var response types.IDResponse
resp, err := cli.post(ctx, "/commit", query, options.Config, nil) resp, err := cli.post(ctx, "/commit", query, options.Config, nil)
defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return response, err return response, err
} }
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.body).Decode(&response)
ensureReaderClosed(resp)
return response, err return response, err
} }

View File

@ -21,10 +21,10 @@ func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path stri
urlStr := "/containers/" + containerID + "/archive" urlStr := "/containers/" + containerID + "/archive"
response, err := cli.head(ctx, urlStr, query, nil) response, err := cli.head(ctx, urlStr, query, nil)
defer ensureReaderClosed(response)
if err != nil { if err != nil {
return types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+path) return types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+path)
} }
defer ensureReaderClosed(response)
return getContainerPathStatFromHeader(response.header) return getContainerPathStatFromHeader(response.header)
} }
@ -45,11 +45,12 @@ func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath str
apiPath := "/containers/" + containerID + "/archive" apiPath := "/containers/" + containerID + "/archive"
response, err := cli.putRaw(ctx, apiPath, query, content, nil) response, err := cli.putRaw(ctx, apiPath, query, content, nil)
defer ensureReaderClosed(response)
if err != nil { if err != nil {
return wrapResponseError(err, response, "container:path", containerID+":"+dstPath) return wrapResponseError(err, response, "container:path", containerID+":"+dstPath)
} }
defer ensureReaderClosed(response)
// TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior
if response.statusCode != http.StatusOK { if response.statusCode != http.StatusOK {
return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode) return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
} }
@ -69,6 +70,7 @@ func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath s
return nil, types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+srcPath) return nil, types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+srcPath)
} }
// TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior
if response.statusCode != http.StatusOK { if response.statusCode != http.StatusOK {
return nil, types.ContainerPathStat{}, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode) return nil, types.ContainerPathStat{}, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
} }

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/url" "net/url"
"strings"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
@ -43,14 +42,11 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
} }
serverResp, err := cli.post(ctx, "/containers/create", query, body, nil) serverResp, err := cli.post(ctx, "/containers/create", query, body, nil)
defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
if serverResp.statusCode == 404 && strings.Contains(err.Error(), "No such image") {
return response, objectNotFoundError{object: "image", id: config.Image}
}
return response, err return response, err
} }
err = json.NewDecoder(serverResp.body).Decode(&response) err = json.NewDecoder(serverResp.body).Decode(&response)
ensureReaderClosed(serverResp)
return response, err return response, err
} }

View File

@ -13,11 +13,11 @@ func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]con
var changes []container.ContainerChangeResponseItem var changes []container.ContainerChangeResponseItem
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil) serverResp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil)
defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return changes, err return changes, err
} }
err = json.NewDecoder(serverResp.body).Decode(&changes) err = json.NewDecoder(serverResp.body).Decode(&changes)
ensureReaderClosed(serverResp)
return changes, err return changes, err
} }

View File

@ -16,11 +16,11 @@ func (cli *Client) ContainerExecCreate(ctx context.Context, container string, co
} }
resp, err := cli.post(ctx, "/containers/"+container+"/exec", nil, config, nil) resp, err := cli.post(ctx, "/containers/"+container+"/exec", nil, config, nil)
defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return response, err return response, err
} }
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.body).Decode(&response)
ensureReaderClosed(resp)
return response, err return response, err
} }

View File

@ -16,13 +16,13 @@ func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (ty
return types.ContainerJSON{}, objectNotFoundError{object: "container", id: containerID} return types.ContainerJSON{}, objectNotFoundError{object: "container", id: containerID}
} }
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil) serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil)
defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return types.ContainerJSON{}, wrapResponseError(err, serverResp, "container", containerID) return types.ContainerJSON{}, wrapResponseError(err, serverResp, "container", containerID)
} }
var response types.ContainerJSON var response types.ContainerJSON
err = json.NewDecoder(serverResp.body).Decode(&response) err = json.NewDecoder(serverResp.body).Decode(&response)
ensureReaderClosed(serverResp)
return response, err return response, err
} }
@ -36,10 +36,10 @@ func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID stri
query.Set("size", "1") query.Set("size", "1")
} }
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", query, nil) serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", query, nil)
defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return types.ContainerJSON{}, nil, wrapResponseError(err, serverResp, "container", containerID) return types.ContainerJSON{}, nil, wrapResponseError(err, serverResp, "container", containerID)
} }
defer ensureReaderClosed(serverResp)
body, err := ioutil.ReadAll(serverResp.body) body, err := ioutil.ReadAll(serverResp.body)
if err != nil { if err != nil {

View File

@ -45,12 +45,12 @@ func (cli *Client) ContainerList(ctx context.Context, options types.ContainerLis
} }
resp, err := cli.get(ctx, "/containers/json", query, nil) resp, err := cli.get(ctx, "/containers/json", query, nil)
defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var containers []types.Container var containers []types.Container
err = json.NewDecoder(resp.body).Decode(&containers) err = json.NewDecoder(resp.body).Decode(&containers)
ensureReaderClosed(resp)
return containers, err return containers, err
} }

View File

@ -23,10 +23,10 @@ func (cli *Client) ContainersPrune(ctx context.Context, pruneFilters filters.Arg
} }
serverResp, err := cli.post(ctx, "/containers/prune", query, nil, nil) serverResp, err := cli.post(ctx, "/containers/prune", query, nil, nil)
defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return report, err return report, err
} }
defer ensureReaderClosed(serverResp)
if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil {
return report, fmt.Errorf("Error retrieving disk usage: %v", err) return report, fmt.Errorf("Error retrieving disk usage: %v", err)

View File

@ -22,6 +22,6 @@ func (cli *Client) ContainerRemove(ctx context.Context, containerID string, opti
} }
resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil) resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil)
ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "container", containerID) return wrapResponseError(err, resp, "container", containerID)
} }

View File

@ -18,11 +18,11 @@ func (cli *Client) ContainerTop(ctx context.Context, containerID string, argumen
} }
resp, err := cli.get(ctx, "/containers/"+containerID+"/top", query, nil) resp, err := cli.get(ctx, "/containers/"+containerID+"/top", query, nil)
defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return response, err return response, err
} }
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.body).Decode(&response)
ensureReaderClosed(resp)
return response, err return response, err
} }

View File

@ -11,12 +11,11 @@ import (
func (cli *Client) ContainerUpdate(ctx context.Context, containerID string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error) { func (cli *Client) ContainerUpdate(ctx context.Context, containerID string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error) {
var response container.ContainerUpdateOKBody var response container.ContainerUpdateOKBody
serverResp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, updateConfig, nil) serverResp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, updateConfig, nil)
defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return response, err return response, err
} }
err = json.NewDecoder(serverResp.body).Decode(&response) err = json.NewDecoder(serverResp.body).Decode(&response)
ensureReaderClosed(serverResp)
return response, err return response, err
} }

View File

@ -13,10 +13,10 @@ func (cli *Client) DiskUsage(ctx context.Context) (types.DiskUsage, error) {
var du types.DiskUsage var du types.DiskUsage
serverResp, err := cli.get(ctx, "/system/df", nil, nil) serverResp, err := cli.get(ctx, "/system/df", nil, nil)
defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return du, err return du, err
} }
defer ensureReaderClosed(serverResp)
if err := json.NewDecoder(serverResp.body).Decode(&du); err != nil { if err := json.NewDecoder(serverResp.body).Decode(&du); err != nil {
return du, fmt.Errorf("Error retrieving disk usage: %v", err) return du, fmt.Errorf("Error retrieving disk usage: %v", err)

View File

@ -28,11 +28,11 @@ func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegist
} }
resp, err := cli.get(ctx, "/distribution/"+image+"/json", url.Values{}, headers) resp, err := cli.get(ctx, "/distribution/"+image+"/json", url.Values{}, headers)
defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return distributionInspect, err return distributionInspect, err
} }
err = json.NewDecoder(resp.body).Decode(&distributionInspect) err = json.NewDecoder(resp.body).Decode(&distributionInspect)
ensureReaderClosed(resp)
return distributionInspect, err return distributionInspect, err
} }

View File

@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions"
"github.com/docker/docker/errdefs"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -32,16 +33,19 @@ func ErrorConnectionFailed(host string) error {
return errConnectionFailed{host: host} return errConnectionFailed{host: host}
} }
// Deprecated: use the errdefs.NotFound() interface instead. Kept for backward compatibility
type notFound interface { type notFound interface {
error error
NotFound() bool // Is the error a NotFound error NotFound() bool
} }
// IsErrNotFound returns true if the error is a NotFound error, which is returned // IsErrNotFound returns true if the error is a NotFound error, which is returned
// by the API when some object is not found. // by the API when some object is not found.
func IsErrNotFound(err error) bool { func IsErrNotFound(err error) bool {
te, ok := err.(notFound) if _, ok := err.(notFound); ok {
return ok && te.NotFound() return ok
}
return errdefs.IsNotFound(err)
} }
type objectNotFoundError struct { type objectNotFoundError struct {
@ -49,9 +53,7 @@ type objectNotFoundError struct {
id string id string
} }
func (e objectNotFoundError) NotFound() bool { func (e objectNotFoundError) NotFound() {}
return true
}
func (e objectNotFoundError) Error() string { func (e objectNotFoundError) Error() string {
return fmt.Sprintf("Error: No such %s: %s", e.object, e.id) return fmt.Sprintf("Error: No such %s: %s", e.object, e.id)
@ -64,7 +66,7 @@ func wrapResponseError(err error, resp serverResponse, object, id string) error
case resp.statusCode == http.StatusNotFound: case resp.statusCode == http.StatusNotFound:
return objectNotFoundError{object: object, id: id} return objectNotFoundError{object: object, id: id}
case resp.statusCode == http.StatusNotImplemented: case resp.statusCode == http.StatusNotImplemented:
return notImplementedError{message: err.Error()} return errdefs.NotImplemented(err)
default: default:
return err return err
} }
@ -83,8 +85,10 @@ func (u unauthorizedError) Error() string {
// IsErrUnauthorized returns true if the error is caused // IsErrUnauthorized returns true if the error is caused
// when a remote registry authentication fails // when a remote registry authentication fails
func IsErrUnauthorized(err error) bool { func IsErrUnauthorized(err error) bool {
_, ok := err.(unauthorizedError) if _, ok := err.(unauthorizedError); ok {
return ok return ok
}
return errdefs.IsUnauthorized(err)
} }
type pluginPermissionDenied struct { type pluginPermissionDenied struct {
@ -118,8 +122,10 @@ func (e notImplementedError) NotImplemented() bool {
// This is returned by the API when a requested feature has not been // This is returned by the API when a requested feature has not been
// implemented. // implemented.
func IsErrNotImplemented(err error) bool { func IsErrNotImplemented(err error) bool {
te, ok := err.(notImplementedError) if _, ok := err.(notImplementedError); ok {
return ok && te.NotImplemented() return ok
}
return errdefs.IsNotImplemented(err)
} }
// NewVersionError returns an error if the APIVersion required // NewVersionError returns an error if the APIVersion required

View File

@ -30,7 +30,7 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
} }
req = cli.addHeaders(req, headers) req = cli.addHeaders(req, headers)
conn, err := cli.setupHijackConn(req, "tcp") conn, err := cli.setupHijackConn(ctx, req, "tcp")
if err != nil { if err != nil {
return types.HijackedResponse{}, err return types.HijackedResponse{}, err
} }
@ -38,7 +38,20 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
return types.HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn)}, err return types.HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn)}, err
} }
func dial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) { // DialHijack returns a hijacked connection with negotiated protocol proto.
func (cli *Client) DialHijack(ctx context.Context, url, proto string, meta map[string][]string) (net.Conn, error) {
req, err := http.NewRequest("POST", url, nil)
if err != nil {
return nil, err
}
req = cli.addHeaders(req, meta)
return cli.setupHijackConn(ctx, req, proto)
}
// fallbackDial is used when WithDialer() was not called.
// See cli.Dialer().
func fallbackDial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) {
if tlsConfig != nil && proto != "unix" && proto != "npipe" { if tlsConfig != nil && proto != "unix" && proto != "npipe" {
return tls.Dial(proto, addr, tlsConfig) return tls.Dial(proto, addr, tlsConfig)
} }
@ -48,12 +61,13 @@ func dial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) {
return net.Dial(proto, addr) return net.Dial(proto, addr)
} }
func (cli *Client) setupHijackConn(req *http.Request, proto string) (net.Conn, error) { func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto string) (net.Conn, error) {
req.Host = cli.addr req.Host = cli.addr
req.Header.Set("Connection", "Upgrade") req.Header.Set("Connection", "Upgrade")
req.Header.Set("Upgrade", proto) req.Header.Set("Upgrade", proto)
conn, err := dial(cli.proto, cli.addr, resolveTLSConfig(cli.client.Transport)) dialer := cli.Dialer()
conn, err := dialer(ctx)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "cannot connect to the Docker daemon. Is 'docker daemon' running on this host?") return nil, errors.Wrap(err, "cannot connect to the Docker daemon. Is 'docker daemon' running on this host?")
} }

View File

@ -30,12 +30,6 @@ func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, optio
} }
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf)) headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
if options.Platform != "" {
if err := cli.NewVersionError("1.32", "platform"); err != nil {
return types.ImageBuildResponse{}, err
}
query.Set("platform", options.Platform)
}
headers.Set("Content-Type", "application/x-tar") headers.Set("Content-Type", "application/x-tar")
serverResp, err := cli.postRaw(ctx, "/build", query, buildContext, headers) serverResp, err := cli.postRaw(ctx, "/build", query, buildContext, headers)
@ -131,7 +125,22 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur
query.Set("session", options.SessionID) query.Set("session", options.SessionID)
} }
if options.Platform != "" { if options.Platform != "" {
if err := cli.NewVersionError("1.32", "platform"); err != nil {
return query, err
}
query.Set("platform", strings.ToLower(options.Platform)) query.Set("platform", strings.ToLower(options.Platform))
} }
if options.BuildID != "" {
query.Set("buildid", options.BuildID)
}
query.Set("version", string(options.Version))
if options.Outputs != nil {
outputsJSON, err := json.Marshal(options.Outputs)
if err != nil {
return query, err
}
query.Set("outputs", string(outputsJSON))
}
return query, nil return query, nil
} }

View File

@ -12,11 +12,11 @@ import (
func (cli *Client) ImageHistory(ctx context.Context, imageID string) ([]image.HistoryResponseItem, error) { func (cli *Client) ImageHistory(ctx context.Context, imageID string) ([]image.HistoryResponseItem, error) {
var history []image.HistoryResponseItem var history []image.HistoryResponseItem
serverResp, err := cli.get(ctx, "/images/"+imageID+"/history", url.Values{}, nil) serverResp, err := cli.get(ctx, "/images/"+imageID+"/history", url.Values{}, nil)
defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return history, err return history, err
} }
err = json.NewDecoder(serverResp.body).Decode(&history) err = json.NewDecoder(serverResp.body).Decode(&history)
ensureReaderClosed(serverResp)
return history, err return history, err
} }

View File

@ -15,10 +15,10 @@ func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (typ
return types.ImageInspect{}, nil, objectNotFoundError{object: "image", id: imageID} return types.ImageInspect{}, nil, objectNotFoundError{object: "image", id: imageID}
} }
serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil) serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil)
defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return types.ImageInspect{}, nil, wrapResponseError(err, serverResp, "image", imageID) return types.ImageInspect{}, nil, wrapResponseError(err, serverResp, "image", imageID)
} }
defer ensureReaderClosed(serverResp)
body, err := ioutil.ReadAll(serverResp.body) body, err := ioutil.ReadAll(serverResp.body)
if err != nil { if err != nil {

View File

@ -35,11 +35,11 @@ func (cli *Client) ImageList(ctx context.Context, options types.ImageListOptions
} }
serverResp, err := cli.get(ctx, "/images/json", query, nil) serverResp, err := cli.get(ctx, "/images/json", query, nil)
defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return images, err return images, err
} }
err = json.NewDecoder(serverResp.body).Decode(&images) err = json.NewDecoder(serverResp.body).Decode(&images)
ensureReaderClosed(serverResp)
return images, err return images, err
} }

View File

@ -23,10 +23,10 @@ func (cli *Client) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (
} }
serverResp, err := cli.post(ctx, "/images/prune", query, nil, nil) serverResp, err := cli.post(ctx, "/images/prune", query, nil, nil)
defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return report, err return report, err
} }
defer ensureReaderClosed(serverResp)
if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil {
return report, fmt.Errorf("Error retrieving disk usage: %v", err) return report, fmt.Errorf("Error retrieving disk usage: %v", err)

View File

@ -3,12 +3,12 @@ package client // import "github.com/docker/docker/client"
import ( import (
"context" "context"
"io" "io"
"net/http"
"net/url" "net/url"
"strings" "strings"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/errdefs"
) )
// ImagePull requests the docker host to pull an image from a remote registry. // ImagePull requests the docker host to pull an image from a remote registry.
@ -35,7 +35,7 @@ func (cli *Client) ImagePull(ctx context.Context, refStr string, options types.I
} }
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth) resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil { if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
newAuthHeader, privilegeErr := options.PrivilegeFunc() newAuthHeader, privilegeErr := options.PrivilegeFunc()
if privilegeErr != nil { if privilegeErr != nil {
return nil, privilegeErr return nil, privilegeErr

View File

@ -4,11 +4,11 @@ import (
"context" "context"
"errors" "errors"
"io" "io"
"net/http"
"net/url" "net/url"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/errdefs"
) )
// ImagePush requests the docker host to push an image to a remote registry. // ImagePush requests the docker host to push an image to a remote registry.
@ -36,7 +36,7 @@ func (cli *Client) ImagePush(ctx context.Context, image string, options types.Im
query.Set("tag", tag) query.Set("tag", tag)
resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth) resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth)
if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil { if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
newAuthHeader, privilegeErr := options.PrivilegeFunc() newAuthHeader, privilegeErr := options.PrivilegeFunc()
if privilegeErr != nil { if privilegeErr != nil {
return nil, privilegeErr return nil, privilegeErr

View File

@ -21,11 +21,11 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options type
var dels []types.ImageDeleteResponseItem var dels []types.ImageDeleteResponseItem
resp, err := cli.delete(ctx, "/images/"+imageID, query, nil) resp, err := cli.delete(ctx, "/images/"+imageID, query, nil)
defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return dels, wrapResponseError(err, resp, "image", imageID) return dels, wrapResponseError(err, resp, "image", imageID)
} }
err = json.NewDecoder(resp.body).Decode(&dels) err = json.NewDecoder(resp.body).Decode(&dels)
ensureReaderClosed(resp)
return dels, err return dels, err
} }

Some files were not shown because too many files have changed in this diff Show More