Alpine base image and some dependencies upgraded
Details in the reweave CHANGELOG
This commit is contained in:
1 parent
1ceeca80e6
commit
0d58e17954
368 files changed
+23406
-9163
No files matched your search
@@ -10,10 +10,10 @@ require (
|
||||
github.com/containernetworking/cni v1.1.2
|
||||
github.com/containernetworking/plugins v1.2.0
|
||||
github.com/coreos/go-iptables v0.6.0
|
||||
github.com/docker/docker v23.0.3+incompatible
|
||||
github.com/docker/docker v24.0.7+incompatible
|
||||
github.com/docker/libnetwork v0.8.0-dev.2.0.20180212220201-119badbd737b
|
||||
github.com/docker/machine v0.14.0
|
||||
github.com/fsouza/go-dockerclient v1.2.0
|
||||
github.com/fsouza/go-dockerclient v1.10.0
|
||||
github.com/google/gopacket v1.1.16
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56
|
||||
@@ -30,8 +30,8 @@ require (
|
||||
github.com/weaveworks/go-checkpoint v0.0.0-20170503165305-ebbb8b0518ab
|
||||
github.com/weaveworks/go-odp v0.0.0-20181017121109-6b0aa22550d9
|
||||
github.com/weaveworks/mesh v0.0.0-20191105120815-58dbcc3e8e63
|
||||
golang.org/x/crypto v0.1.0
|
||||
golang.org/x/sys v0.6.0
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/sys v0.13.0
|
||||
golang.org/x/tools v0.7.0
|
||||
k8s.io/api v0.26.2
|
||||
k8s.io/apimachinery v0.26.2
|
||||
@@ -44,7 +44,7 @@ require github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/containerd/containerd v1.7.0 // indirect
|
||||
@@ -74,7 +74,7 @@ require (
|
||||
github.com/mattn/go-colorable v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mdlayher/raw v0.0.0-20181016155347-fa5ef3332ca9 // indirect
|
||||
github.com/moby/patternmatcher v0.5.0 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
@@ -92,10 +92,10 @@ require (
|
||||
github.com/safchain/ethtool v0.2.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/mod v0.9.0 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/oauth2 v0.4.0 // indirect
|
||||
golang.org/x/term v0.6.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
golang.org/x/term v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
|
||||
@@ -31,12 +31,13 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/Microsoft/hcsshim v0.10.0-rc.7 h1:HBytQPxcv8Oy4244zbQbe6hnOnx544eL5QPUqhJldz8=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@@ -70,7 +71,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||
github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg=
|
||||
github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc=
|
||||
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
|
||||
github.com/containernetworking/cni v1.1.2 h1:wtRGZVv7olUHMOqouPpn3cXJWpJgM6+EUl31EQbXALQ=
|
||||
github.com/containernetworking/cni v1.1.2/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw=
|
||||
github.com/containernetworking/plugins v1.2.0 h1:SWgg3dQG1yzUo4d9iD8cwSVh1VqI+bP7mkPDoSfP9VU=
|
||||
@@ -82,14 +82,15 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
|
||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/distribution v2.8.2-beta.1+incompatible h1:gILO60VLD2v28ozemv4aAwDb8ds5U2O/vD/sBXbd7Rw=
|
||||
github.com/docker/distribution v2.8.2-beta.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho=
|
||||
github.com/docker/docker v23.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
|
||||
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
@@ -112,8 +113,8 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsouza/go-dockerclient v1.2.0 h1:lmjnUbZFR+De5PfuXCk62hAIlkokNtD9CaGPR8Iu5UU=
|
||||
github.com/fsouza/go-dockerclient v1.2.0/go.mod h1:KpcjM623fQYE9MZiTGzKhjfxXAV9wbyX2C1cyRHfhl0=
|
||||
github.com/fsouza/go-dockerclient v1.10.0 h1:ppSBsbR60I1DFbV4Ag7LlHlHakHFRNLk9XakATW1yVQ=
|
||||
github.com/fsouza/go-dockerclient v1.10.0/go.mod h1:+iNzAW78AzClIBTZ6WFjkaMvOgz68GyCJ236b1opLTs=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -266,8 +267,8 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c=
|
||||
github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo=
|
||||
github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
|
||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||
@@ -411,8 +412,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -481,8 +482,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -558,12 +559,13 @@ golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -571,8 +573,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -6,6 +6,16 @@ All changes made to the weave net codebase during the reweave effort will be doc
|
||||
|
||||
### Changed
|
||||
|
||||
* Alpine base image upgraded to alpine:2.18.4, and `apk upgrade` applied
|
||||
* Upgraded golang.org/x/net to v0.17.0. This upgraded some dependecies
|
||||
* Upgraded github.com/docker/docker to v24.0.7. This caused a compilation error
|
||||
* Upgraded github.com/fsouza/go-dockerclient to v1.10.0. This resolved the error
|
||||
|
||||
## 2.8.2-beta10 (126f3ab9)
|
||||
|
||||
### Changed
|
||||
|
||||
* Alpine base image upgraded to alpine:2.18.3
|
||||
* Updated documentation
|
||||
* Changed [reweave/tool/build-images.sh](reweave/tool/build-images.sh) to be consistent with documentation
|
||||
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
IMAGE_VERSION ?= 2.8.2-beta10
|
||||
IMAGE_VERSION ?= 2.8.2-beta11
|
||||
REGISTRY_USER ?= weaveworks
|
||||
|
||||
ALPINE_BASEIMAGE ?= alpine:3.18.3
|
||||
ALPINE_BASEIMAGE ?= alpine:3.18.4
|
||||
|
||||
# Targets
|
||||
.PHONY: build-images
|
||||
|
||||
@@ -79,6 +79,10 @@ EOMAKE
|
||||
# required in the alpine base image.
|
||||
FROM --platform=linux/${TARGETARCH} ${ALPINE_BASEIMAGE} as alpinebase
|
||||
# Any additional tweaks will appear here
|
||||
# Last update on top of alpine:3.18.4
|
||||
RUN apk update \
|
||||
&& apk upgrade \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
# End Stage 3
|
||||
########
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
Image version: 2.8.2-beta10
|
||||
Image version: 2.8.2-beta11
|
||||
@@ -1 +1 @@
|
||||
weave-kube CVE Count: 18
|
||||
weave-kube CVE Count: 0
|
||||
@@ -1,20 +1 @@
|
||||
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
|
||||
bind-libs 9.18.16-r0 9.18.19-r0 apk CVE-2023-4236 High
|
||||
bind-libs 9.18.16-r0 9.18.19-r0 apk CVE-2023-3341 High
|
||||
bind-tools 9.18.16-r0 9.18.19-r0 apk CVE-2023-4236 High
|
||||
bind-tools 9.18.16-r0 9.18.19-r0 apk CVE-2023-3341 High
|
||||
curl 8.2.1-r0 8.4.0-r0 apk CVE-2023-38545 Critical
|
||||
curl 8.2.1-r0 8.3.0-r0 apk CVE-2023-38039 High
|
||||
curl 8.2.1-r0 8.4.0-r0 apk CVE-2023-38546 Low
|
||||
github.com/docker/docker v23.0.3+incompatible 24.0.7 go-module GHSA-jq35-85cj-fj4p Medium
|
||||
golang.org/x/net v0.8.0 0.17.0 go-module GHSA-4374-p667-p6c8 High
|
||||
golang.org/x/net v0.8.0 0.17.0 go-module GHSA-qppj-fm5r-hxr3 Medium
|
||||
golang.org/x/net v0.8.0 0.13.0 go-module GHSA-2wrh-6pvc-2jm9 Medium
|
||||
libcrypto3 3.1.2-r0 3.1.4-r0 apk CVE-2023-5363 High
|
||||
libcrypto3 3.1.2-r0 3.1.4-r1 apk CVE-2023-5678 Unknown
|
||||
libcurl 8.2.1-r0 8.4.0-r0 apk CVE-2023-38545 Critical
|
||||
libcurl 8.2.1-r0 8.3.0-r0 apk CVE-2023-38039 High
|
||||
libcurl 8.2.1-r0 8.4.0-r0 apk CVE-2023-38546 Low
|
||||
libssl3 3.1.2-r0 3.1.4-r0 apk CVE-2023-5363 High
|
||||
libssl3 3.1.2-r0 3.1.4-r1 apk CVE-2023-5678 Unknown
|
||||
nghttp2-libs 1.55.1-r0 1.57.0-r0 apk CVE-2023-44487 High
|
||||
No vulnerabilities found
|
||||
@@ -1 +1 @@
|
||||
weave-npc CVE Count: 6
|
||||
weave-npc CVE Count: 0
|
||||
@@ -1,8 +1 @@
|
||||
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
|
||||
golang.org/x/net v0.8.0 0.17.0 go-module GHSA-4374-p667-p6c8 High
|
||||
golang.org/x/net v0.8.0 0.17.0 go-module GHSA-qppj-fm5r-hxr3 Medium
|
||||
golang.org/x/net v0.8.0 0.13.0 go-module GHSA-2wrh-6pvc-2jm9 Medium
|
||||
libcrypto3 3.1.2-r0 3.1.4-r0 apk CVE-2023-5363 High
|
||||
libcrypto3 3.1.2-r0 3.1.4-r1 apk CVE-2023-5678 Unknown
|
||||
libssl3 3.1.2-r0 3.1.4-r0 apk CVE-2023-5363 High
|
||||
libssl3 3.1.2-r0 3.1.4-r1 apk CVE-2023-5678 Unknown
|
||||
No vulnerabilities found
|
||||
+16
-11
@@ -8,12 +8,8 @@ linters:
|
||||
- containedctx # struct contains a context
|
||||
- dupl # duplicate code
|
||||
- errname # erorrs are named correctly
|
||||
- goconst # strings that should be constants
|
||||
- godot # comments end in a period
|
||||
- misspell
|
||||
- nolintlint # "//nolint" directives are properly explained
|
||||
- revive # golint replacement
|
||||
- stylecheck # golint replacement, less configurable than revive
|
||||
- unconvert # unnecessary conversions
|
||||
- wastedassign
|
||||
|
||||
@@ -23,10 +19,7 @@ linters:
|
||||
- exhaustive # check exhaustiveness of enum switch statements
|
||||
- gofmt # files are gofmt'ed
|
||||
- gosec # security
|
||||
- nestif # deeply nested ifs
|
||||
- nilerr # returns nil even with non-nil error
|
||||
- prealloc # slices that can be pre-allocated
|
||||
- structcheck # unused struct fields
|
||||
- unparam # unused function params
|
||||
|
||||
issues:
|
||||
@@ -42,6 +35,18 @@ issues:
|
||||
text: "^line-length-limit: "
|
||||
source: "^//(go:generate|sys) "
|
||||
|
||||
#TODO: remove after upgrading to go1.18
|
||||
# ignore comment spacing for nolint and sys directives
|
||||
- linters:
|
||||
- revive
|
||||
text: "^comment-spacings: no space between comment delimiter and comment text"
|
||||
source: "//(cspell:|nolint:|sys |todo)"
|
||||
|
||||
# not on go 1.18 yet, so no any
|
||||
- linters:
|
||||
- revive
|
||||
text: "^use-any: since GO 1.18 'interface{}' can be replaced by 'any'"
|
||||
|
||||
# allow unjustified ignores of error checks in defer statements
|
||||
- linters:
|
||||
- nolintlint
|
||||
@@ -56,6 +61,8 @@ issues:
|
||||
|
||||
|
||||
linters-settings:
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
govet:
|
||||
enable-all: true
|
||||
disable:
|
||||
@@ -98,6 +105,8 @@ linters-settings:
|
||||
disabled: true
|
||||
- name: flag-parameter # excessive, and a common idiom we use
|
||||
disabled: true
|
||||
- name: unhandled-error # warns over common fmt.Print* and io.Close; rely on errcheck instead
|
||||
disabled: true
|
||||
# general config
|
||||
- name: line-length-limit
|
||||
arguments:
|
||||
@@ -138,7 +147,3 @@ linters-settings:
|
||||
- VPCI
|
||||
- WCOW
|
||||
- WIM
|
||||
stylecheck:
|
||||
checks:
|
||||
- "all"
|
||||
- "-ST1003" # use revive's var naming
|
||||
+3
-3
@@ -23,7 +23,7 @@ import (
|
||||
const afHVSock = 34 // AF_HYPERV
|
||||
|
||||
// Well known Service and VM IDs
|
||||
//https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service#vmid-wildcards
|
||||
// https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service#vmid-wildcards
|
||||
|
||||
// HvsockGUIDWildcard is the wildcard VmId for accepting connections from all partitions.
|
||||
func HvsockGUIDWildcard() guid.GUID { // 00000000-0000-0000-0000-000000000000
|
||||
@@ -31,7 +31,7 @@ func HvsockGUIDWildcard() guid.GUID { // 00000000-0000-0000-0000-000000000000
|
||||
}
|
||||
|
||||
// HvsockGUIDBroadcast is the wildcard VmId for broadcasting sends to all partitions.
|
||||
func HvsockGUIDBroadcast() guid.GUID { //ffffffff-ffff-ffff-ffff-ffffffffffff
|
||||
func HvsockGUIDBroadcast() guid.GUID { // ffffffff-ffff-ffff-ffff-ffffffffffff
|
||||
return guid.GUID{
|
||||
Data1: 0xffffffff,
|
||||
Data2: 0xffff,
|
||||
@@ -246,7 +246,7 @@ func (l *HvsockListener) Accept() (_ net.Conn, err error) {
|
||||
var addrbuf [addrlen * 2]byte
|
||||
|
||||
var bytes uint32
|
||||
err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /*rxdatalen*/, addrlen, addrlen, &bytes, &c.o)
|
||||
err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o)
|
||||
if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil {
|
||||
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// This package contains Win32 filesystem functionality.
|
||||
package fs
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
//go:build windows
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/Microsoft/go-winio/internal/stringbuffer"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
|
||||
//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW
|
||||
|
||||
const NullHandle windows.Handle = 0
|
||||
|
||||
// AccessMask defines standard, specific, and generic rights.
|
||||
//
|
||||
// Bitmask:
|
||||
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
|
||||
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
// +---------------+---------------+-------------------------------+
|
||||
// |G|G|G|G|Resvd|A| StandardRights| SpecificRights |
|
||||
// |R|W|E|A| |S| | |
|
||||
// +-+-------------+---------------+-------------------------------+
|
||||
//
|
||||
// GR Generic Read
|
||||
// GW Generic Write
|
||||
// GE Generic Exectue
|
||||
// GA Generic All
|
||||
// Resvd Reserved
|
||||
// AS Access Security System
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/windows/win32/secauthz/access-mask
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants
|
||||
type AccessMask = windows.ACCESS_MASK
|
||||
|
||||
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||
const (
|
||||
// Not actually any.
|
||||
//
|
||||
// For CreateFile: "query certain metadata such as file, directory, or device attributes without accessing that file or device"
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters
|
||||
FILE_ANY_ACCESS AccessMask = 0
|
||||
|
||||
// Specific Object Access
|
||||
// from ntioapi.h
|
||||
|
||||
FILE_READ_DATA AccessMask = (0x0001) // file & pipe
|
||||
FILE_LIST_DIRECTORY AccessMask = (0x0001) // directory
|
||||
|
||||
FILE_WRITE_DATA AccessMask = (0x0002) // file & pipe
|
||||
FILE_ADD_FILE AccessMask = (0x0002) // directory
|
||||
|
||||
FILE_APPEND_DATA AccessMask = (0x0004) // file
|
||||
FILE_ADD_SUBDIRECTORY AccessMask = (0x0004) // directory
|
||||
FILE_CREATE_PIPE_INSTANCE AccessMask = (0x0004) // named pipe
|
||||
|
||||
FILE_READ_EA AccessMask = (0x0008) // file & directory
|
||||
FILE_READ_PROPERTIES AccessMask = FILE_READ_EA
|
||||
|
||||
FILE_WRITE_EA AccessMask = (0x0010) // file & directory
|
||||
FILE_WRITE_PROPERTIES AccessMask = FILE_WRITE_EA
|
||||
|
||||
FILE_EXECUTE AccessMask = (0x0020) // file
|
||||
FILE_TRAVERSE AccessMask = (0x0020) // directory
|
||||
|
||||
FILE_DELETE_CHILD AccessMask = (0x0040) // directory
|
||||
|
||||
FILE_READ_ATTRIBUTES AccessMask = (0x0080) // all
|
||||
|
||||
FILE_WRITE_ATTRIBUTES AccessMask = (0x0100) // all
|
||||
|
||||
FILE_ALL_ACCESS AccessMask = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
|
||||
FILE_GENERIC_READ AccessMask = (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE)
|
||||
FILE_GENERIC_WRITE AccessMask = (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE)
|
||||
FILE_GENERIC_EXECUTE AccessMask = (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE)
|
||||
|
||||
SPECIFIC_RIGHTS_ALL AccessMask = 0x0000FFFF
|
||||
|
||||
// Standard Access
|
||||
// from ntseapi.h
|
||||
|
||||
DELETE AccessMask = 0x0001_0000
|
||||
READ_CONTROL AccessMask = 0x0002_0000
|
||||
WRITE_DAC AccessMask = 0x0004_0000
|
||||
WRITE_OWNER AccessMask = 0x0008_0000
|
||||
SYNCHRONIZE AccessMask = 0x0010_0000
|
||||
|
||||
STANDARD_RIGHTS_REQUIRED AccessMask = 0x000F_0000
|
||||
|
||||
STANDARD_RIGHTS_READ AccessMask = READ_CONTROL
|
||||
STANDARD_RIGHTS_WRITE AccessMask = READ_CONTROL
|
||||
STANDARD_RIGHTS_EXECUTE AccessMask = READ_CONTROL
|
||||
|
||||
STANDARD_RIGHTS_ALL AccessMask = 0x001F_0000
|
||||
)
|
||||
|
||||
type FileShareMode uint32
|
||||
|
||||
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||
const (
|
||||
FILE_SHARE_NONE FileShareMode = 0x00
|
||||
FILE_SHARE_READ FileShareMode = 0x01
|
||||
FILE_SHARE_WRITE FileShareMode = 0x02
|
||||
FILE_SHARE_DELETE FileShareMode = 0x04
|
||||
FILE_SHARE_VALID_FLAGS FileShareMode = 0x07
|
||||
)
|
||||
|
||||
type FileCreationDisposition uint32
|
||||
|
||||
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||
const (
|
||||
// from winbase.h
|
||||
|
||||
CREATE_NEW FileCreationDisposition = 0x01
|
||||
CREATE_ALWAYS FileCreationDisposition = 0x02
|
||||
OPEN_EXISTING FileCreationDisposition = 0x03
|
||||
OPEN_ALWAYS FileCreationDisposition = 0x04
|
||||
TRUNCATE_EXISTING FileCreationDisposition = 0x05
|
||||
)
|
||||
|
||||
// CreateFile and co. take flags or attributes together as one parameter.
|
||||
// Define alias until we can use generics to allow both
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
||||
type FileFlagOrAttribute uint32
|
||||
|
||||
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||
const ( // from winnt.h
|
||||
FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000
|
||||
FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000
|
||||
FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000
|
||||
FILE_FLAG_RANDOM_ACCESS FileFlagOrAttribute = 0x1000_0000
|
||||
FILE_FLAG_SEQUENTIAL_SCAN FileFlagOrAttribute = 0x0800_0000
|
||||
FILE_FLAG_DELETE_ON_CLOSE FileFlagOrAttribute = 0x0400_0000
|
||||
FILE_FLAG_BACKUP_SEMANTICS FileFlagOrAttribute = 0x0200_0000
|
||||
FILE_FLAG_POSIX_SEMANTICS FileFlagOrAttribute = 0x0100_0000
|
||||
FILE_FLAG_OPEN_REPARSE_POINT FileFlagOrAttribute = 0x0020_0000
|
||||
FILE_FLAG_OPEN_NO_RECALL FileFlagOrAttribute = 0x0010_0000
|
||||
FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000
|
||||
)
|
||||
|
||||
type FileSQSFlag = FileFlagOrAttribute
|
||||
|
||||
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||
const ( // from winbase.h
|
||||
SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16)
|
||||
SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16)
|
||||
SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16)
|
||||
SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16)
|
||||
|
||||
SECURITY_SQOS_PRESENT FileSQSFlag = 0x00100000
|
||||
SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F0000
|
||||
)
|
||||
|
||||
// GetFinalPathNameByHandle flags
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew#parameters
|
||||
type GetFinalPathFlag uint32
|
||||
|
||||
//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
|
||||
const (
|
||||
GetFinalPathDefaultFlag GetFinalPathFlag = 0x0
|
||||
|
||||
FILE_NAME_NORMALIZED GetFinalPathFlag = 0x0
|
||||
FILE_NAME_OPENED GetFinalPathFlag = 0x8
|
||||
|
||||
VOLUME_NAME_DOS GetFinalPathFlag = 0x0
|
||||
VOLUME_NAME_GUID GetFinalPathFlag = 0x1
|
||||
VOLUME_NAME_NT GetFinalPathFlag = 0x2
|
||||
VOLUME_NAME_NONE GetFinalPathFlag = 0x4
|
||||
)
|
||||
|
||||
// getFinalPathNameByHandle facilitates calling the Windows API GetFinalPathNameByHandle
|
||||
// with the given handle and flags. It transparently takes care of creating a buffer of the
|
||||
// correct size for the call.
|
||||
//
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew
|
||||
func GetFinalPathNameByHandle(h windows.Handle, flags GetFinalPathFlag) (string, error) {
|
||||
b := stringbuffer.NewWString()
|
||||
//TODO: can loop infinitely if Win32 keeps returning the same (or a larger) n?
|
||||
for {
|
||||
n, err := windows.GetFinalPathNameByHandle(h, b.Pointer(), b.Cap(), uint32(flags))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// If the buffer wasn't large enough, n will be the total size needed (including null terminator).
|
||||
// Resize and try again.
|
||||
if n > b.Cap() {
|
||||
b.ResizeTo(n)
|
||||
continue
|
||||
}
|
||||
// If the buffer is large enough, n will be the size not including the null terminator.
|
||||
// Convert to a Go string and return.
|
||||
return b.String(), nil
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package fs
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
|
||||
type SecurityImpersonationLevel int32 // C default enums underlying type is `int`, which is Go `int32`
|
||||
|
||||
// Impersonation levels
|
||||
const (
|
||||
SecurityAnonymous SecurityImpersonationLevel = 0
|
||||
SecurityIdentification SecurityImpersonationLevel = 1
|
||||
SecurityImpersonation SecurityImpersonationLevel = 2
|
||||
SecurityDelegation SecurityImpersonationLevel = 3
|
||||
)
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
//go:build windows
|
||||
|
||||
// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
errERROR_EINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return errERROR_EINVAL
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
|
||||
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||
)
|
||||
|
||||
func CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||
}
|
||||
|
||||
func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
||||
handle = windows.Handle(r0)
|
||||
if handle == windows.InvalidHandle {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
+2
-2
@@ -100,8 +100,8 @@ func (f *runtimeFunc) Load() error {
|
||||
(*byte)(unsafe.Pointer(&f.addr)),
|
||||
uint32(unsafe.Sizeof(f.addr)),
|
||||
&n,
|
||||
nil, //overlapped
|
||||
0, //completionRoutine
|
||||
nil, // overlapped
|
||||
0, // completionRoutine
|
||||
)
|
||||
})
|
||||
return f.err
|
||||
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
package stringbuffer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
// TODO: worth exporting and using in mkwinsyscall?
|
||||
|
||||
// Uint16BufferSize is the buffer size in the pool, chosen somewhat arbitrarily to accommodate
|
||||
// large path strings:
|
||||
// MAX_PATH (260) + size of volume GUID prefix (49) + null terminator = 310.
|
||||
const MinWStringCap = 310
|
||||
|
||||
// use *[]uint16 since []uint16 creates an extra allocation where the slice header
|
||||
// is copied to heap and then referenced via pointer in the interface header that sync.Pool
|
||||
// stores.
|
||||
var pathPool = sync.Pool{ // if go1.18+ adds Pool[T], use that to store []uint16 directly
|
||||
New: func() interface{} {
|
||||
b := make([]uint16, MinWStringCap)
|
||||
return &b
|
||||
},
|
||||
}
|
||||
|
||||
func newBuffer() []uint16 { return *(pathPool.Get().(*[]uint16)) }
|
||||
|
||||
// freeBuffer copies the slice header data, and puts a pointer to that in the pool.
|
||||
// This avoids taking a pointer to the slice header in WString, which can be set to nil.
|
||||
func freeBuffer(b []uint16) { pathPool.Put(&b) }
|
||||
|
||||
// WString is a wide string buffer ([]uint16) meant for storing UTF-16 encoded strings
|
||||
// for interacting with Win32 APIs.
|
||||
// Sizes are specified as uint32 and not int.
|
||||
//
|
||||
// It is not thread safe.
|
||||
type WString struct {
|
||||
// type-def allows casting to []uint16 directly, use struct to prevent that and allow adding fields in the future.
|
||||
|
||||
// raw buffer
|
||||
b []uint16
|
||||
}
|
||||
|
||||
// NewWString returns a [WString] allocated from a shared pool with an
|
||||
// initial capacity of at least [MinWStringCap].
|
||||
// Since the buffer may have been previously used, its contents are not guaranteed to be empty.
|
||||
//
|
||||
// The buffer should be freed via [WString.Free]
|
||||
func NewWString() *WString {
|
||||
return &WString{
|
||||
b: newBuffer(),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *WString) Free() {
|
||||
if b.empty() {
|
||||
return
|
||||
}
|
||||
freeBuffer(b.b)
|
||||
b.b = nil
|
||||
}
|
||||
|
||||
// ResizeTo grows the buffer to at least c and returns the new capacity, freeing the
|
||||
// previous buffer back into pool.
|
||||
func (b *WString) ResizeTo(c uint32) uint32 {
|
||||
// allready sufficient (or n is 0)
|
||||
if c <= b.Cap() {
|
||||
return b.Cap()
|
||||
}
|
||||
|
||||
if c <= MinWStringCap {
|
||||
c = MinWStringCap
|
||||
}
|
||||
// allocate at-least double buffer size, as is done in [bytes.Buffer] and other places
|
||||
if c <= 2*b.Cap() {
|
||||
c = 2 * b.Cap()
|
||||
}
|
||||
|
||||
b2 := make([]uint16, c)
|
||||
if !b.empty() {
|
||||
copy(b2, b.b)
|
||||
freeBuffer(b.b)
|
||||
}
|
||||
b.b = b2
|
||||
return c
|
||||
}
|
||||
|
||||
// Buffer returns the underlying []uint16 buffer.
|
||||
func (b *WString) Buffer() []uint16 {
|
||||
if b.empty() {
|
||||
return nil
|
||||
}
|
||||
return b.b
|
||||
}
|
||||
|
||||
// Pointer returns a pointer to the first uint16 in the buffer.
|
||||
// If the [WString.Free] has already been called, the pointer will be nil.
|
||||
func (b *WString) Pointer() *uint16 {
|
||||
if b.empty() {
|
||||
return nil
|
||||
}
|
||||
return &b.b[0]
|
||||
}
|
||||
|
||||
// String returns the returns the UTF-8 encoding of the UTF-16 string in the buffer.
|
||||
//
|
||||
// It assumes that the data is null-terminated.
|
||||
func (b *WString) String() string {
|
||||
// Using [windows.UTF16ToString] would require importing "golang.org/x/sys/windows"
|
||||
// and would make this code Windows-only, which makes no sense.
|
||||
// So copy UTF16ToString code into here.
|
||||
// If other windows-specific code is added, switch to [windows.UTF16ToString]
|
||||
|
||||
s := b.b
|
||||
for i, v := range s {
|
||||
if v == 0 {
|
||||
s = s[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
return string(utf16.Decode(s))
|
||||
}
|
||||
|
||||
// Cap returns the underlying buffer capacity.
|
||||
func (b *WString) Cap() uint32 {
|
||||
if b.empty() {
|
||||
return 0
|
||||
}
|
||||
return b.cap()
|
||||
}
|
||||
|
||||
func (b *WString) cap() uint32 { return uint32(cap(b.b)) }
|
||||
func (b *WString) empty() bool { return b == nil || b.cap() == 0 }
|
||||
+13
-9
@@ -16,11 +16,12 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/Microsoft/go-winio/internal/fs"
|
||||
)
|
||||
|
||||
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
||||
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
||||
//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
|
||||
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
||||
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
|
||||
@@ -163,19 +164,21 @@ func (s pipeAddress) String() string {
|
||||
}
|
||||
|
||||
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
|
||||
func tryDialPipe(ctx context.Context, path *string, access uint32) (syscall.Handle, error) {
|
||||
func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask) (syscall.Handle, error) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return syscall.Handle(0), ctx.Err()
|
||||
default:
|
||||
h, err := createFile(*path,
|
||||
wh, err := fs.CreateFile(*path,
|
||||
access,
|
||||
0,
|
||||
nil,
|
||||
syscall.OPEN_EXISTING,
|
||||
windows.FILE_FLAG_OVERLAPPED|windows.SECURITY_SQOS_PRESENT|windows.SECURITY_ANONYMOUS,
|
||||
0)
|
||||
0, // mode
|
||||
nil, // security attributes
|
||||
fs.OPEN_EXISTING,
|
||||
fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.SECURITY_ANONYMOUS,
|
||||
0, // template file handle
|
||||
)
|
||||
h := syscall.Handle(wh)
|
||||
if err == nil {
|
||||
return h, nil
|
||||
}
|
||||
@@ -219,7 +222,7 @@ func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
|
||||
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
|
||||
var err error
|
||||
var h syscall.Handle
|
||||
h, err = tryDialPipe(ctx, &path, access)
|
||||
h, err = tryDialPipe(ctx, &path, fs.AccessMask(access))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -279,6 +282,7 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
|
||||
}
|
||||
defer localFree(ntPath.Buffer)
|
||||
oa.ObjectName = &ntPath
|
||||
oa.Attributes = windows.OBJ_CASE_INSENSITIVE
|
||||
|
||||
// The security descriptor is only needed for the first pipe.
|
||||
if first {
|
||||
|
||||
-19
@@ -63,7 +63,6 @@ var (
|
||||
procBackupWrite = modkernel32.NewProc("BackupWrite")
|
||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||
@@ -305,24 +304,6 @@ func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||
}
|
||||
|
||||
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == syscall.InvalidHandle {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
||||
newport = syscall.Handle(r0)
|
||||
|
||||
+18
@@ -29,6 +29,7 @@ Adam Pointer <adam.pointer@skybettingandgaming.com>
|
||||
Adam Singer <financeCoding@gmail.com>
|
||||
Adam Walz <adam@adamwalz.net>
|
||||
Adam Williams <awilliams@mirantis.com>
|
||||
AdamKorcz <adam@adalogics.com>
|
||||
Addam Hardy <addam.hardy@gmail.com>
|
||||
Aditi Rajagopal <arajagopal@us.ibm.com>
|
||||
Aditya <aditya@netroy.in>
|
||||
@@ -81,6 +82,7 @@ Alex Goodman <wagoodman@gmail.com>
|
||||
Alex Nordlund <alexander.nordlund@nasdaq.com>
|
||||
Alex Olshansky <i@creagenics.com>
|
||||
Alex Samorukov <samm@os2.kiev.ua>
|
||||
Alex Stockinger <alex@atomicjar.com>
|
||||
Alex Warhawk <ax.warhawk@gmail.com>
|
||||
Alexander Artemenko <svetlyak.40wt@gmail.com>
|
||||
Alexander Boyd <alex@opengroove.org>
|
||||
@@ -198,6 +200,7 @@ Anusha Ragunathan <anusha.ragunathan@docker.com>
|
||||
Anyu Wang <wanganyu@outlook.com>
|
||||
apocas <petermdias@gmail.com>
|
||||
Arash Deshmeh <adeshmeh@ca.ibm.com>
|
||||
arcosx <arcosx@outlook.com>
|
||||
ArikaChen <eaglesora@gmail.com>
|
||||
Arko Dasgupta <arko@tetrate.io>
|
||||
Arnaud Lefebvre <a.lefebvre@outlook.fr>
|
||||
@@ -241,6 +244,7 @@ Benjamin Atkin <ben@benatkin.com>
|
||||
Benjamin Baker <Benjamin.baker@utexas.edu>
|
||||
Benjamin Boudreau <boudreau.benjamin@gmail.com>
|
||||
Benjamin Böhmke <benjamin@boehmke.net>
|
||||
Benjamin Wang <wachao@vmware.com>
|
||||
Benjamin Yolken <yolken@stripe.com>
|
||||
Benny Ng <benny.tpng@gmail.com>
|
||||
Benoit Chesneau <bchesneau@gmail.com>
|
||||
@@ -634,6 +638,7 @@ Eng Zer Jun <engzerjun@gmail.com>
|
||||
Enguerran <engcolson@gmail.com>
|
||||
Eohyung Lee <liquidnuker@gmail.com>
|
||||
epeterso <epeterson@breakpoint-labs.com>
|
||||
er0k <er0k@er0k.net>
|
||||
Eric Barch <barch@tomesoftware.com>
|
||||
Eric Curtin <ericcurtin17@gmail.com>
|
||||
Eric G. Noriega <enoriega@vizuri.com>
|
||||
@@ -754,6 +759,7 @@ Félix Baylac-Jacqué <baylac.felix@gmail.com>
|
||||
Félix Cantournet <felix.cantournet@cloudwatt.com>
|
||||
Gabe Rosenhouse <gabe@missionst.com>
|
||||
Gabor Nagy <mail@aigeruth.hu>
|
||||
Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
|
||||
Gabriel Goller <gabrielgoller123@gmail.com>
|
||||
Gabriel L. Somlo <gsomlo@gmail.com>
|
||||
Gabriel Linder <linder.gabriel@gmail.com>
|
||||
@@ -855,6 +861,7 @@ Hongbin Lu <hongbin034@gmail.com>
|
||||
Hongxu Jia <hongxu.jia@windriver.com>
|
||||
Honza Pokorny <me@honza.ca>
|
||||
Hsing-Hui Hsu <hsinghui@amazon.com>
|
||||
Hsing-Yu (David) Chen <davidhsingyuchen@gmail.com>
|
||||
hsinko <21551195@zju.edu.cn>
|
||||
Hu Keping <hukeping@huawei.com>
|
||||
Hu Tao <hutao@cn.fujitsu.com>
|
||||
@@ -887,6 +894,7 @@ Igor Dolzhikov <bluesriverz@gmail.com>
|
||||
Igor Karpovich <i.karpovich@currencysolutions.com>
|
||||
Iliana Weller <iweller@amazon.com>
|
||||
Ilkka Laukkanen <ilkka@ilkka.io>
|
||||
Illia Antypenko <ilya@antipenko.pp.ua>
|
||||
Illo Abdulrahim <abdulrahim.illo@nokia.com>
|
||||
Ilya Dmitrichenko <errordeveloper@gmail.com>
|
||||
Ilya Gusev <mail@igusev.ru>
|
||||
@@ -938,6 +946,7 @@ Jamie Hannaford <jamie@limetree.org>
|
||||
Jamshid Afshar <jafshar@yahoo.com>
|
||||
Jan Breig <git@pygos.space>
|
||||
Jan Chren <dev.rindeal@gmail.com>
|
||||
Jan Garcia <github-public@n-garcia.com>
|
||||
Jan Götte <jaseg@jaseg.net>
|
||||
Jan Keromnes <janx@linux.com>
|
||||
Jan Koprowski <jan.koprowski@gmail.com>
|
||||
@@ -1206,6 +1215,7 @@ Kimbro Staken <kstaken@kstaken.com>
|
||||
Kir Kolyshkin <kolyshkin@gmail.com>
|
||||
Kiran Gangadharan <kiran.daredevil@gmail.com>
|
||||
Kirill SIbirev <l0kix2@gmail.com>
|
||||
Kirk Easterson <kirk.easterson@gmail.com>
|
||||
knappe <tyler.knappe@gmail.com>
|
||||
Kohei Tsuruta <coheyxyz@gmail.com>
|
||||
Koichi Shiraishi <k@zchee.io>
|
||||
@@ -1240,10 +1250,12 @@ Lars Kellogg-Stedman <lars@redhat.com>
|
||||
Lars R. Damerow <lars@pixar.com>
|
||||
Lars-Magnus Skog <ralphtheninja@riseup.net>
|
||||
Laszlo Meszaros <lacienator@gmail.com>
|
||||
Laura Brehm <laurabrehm@hey.com>
|
||||
Laura Frank <ljfrank@gmail.com>
|
||||
Laurent Bernaille <laurent.bernaille@datadoghq.com>
|
||||
Laurent Erignoux <lerignoux@gmail.com>
|
||||
Laurie Voss <github@seldo.com>
|
||||
Leandro Motta Barros <lmb@stackedboxes.org>
|
||||
Leandro Siqueira <leandro.siqueira@gmail.com>
|
||||
Lee Calcote <leecalcote@gmail.com>
|
||||
Lee Chao <932819864@qq.com>
|
||||
@@ -1563,6 +1575,7 @@ Nick Neisen <nwneisen@gmail.com>
|
||||
Nick Parker <nikaios@gmail.com>
|
||||
Nick Payne <nick@kurai.co.uk>
|
||||
Nick Russo <nicholasjamesrusso@gmail.com>
|
||||
Nick Santos <nick.santos@docker.com>
|
||||
Nick Stenning <nick.stenning@digital.cabinet-office.gov.uk>
|
||||
Nick Stinemates <nick@stinemates.org>
|
||||
Nick Wood <nwood@microsoft.com>
|
||||
@@ -1584,6 +1597,7 @@ NikolaMandic <mn080202@gmail.com>
|
||||
Nikolas Garofil <nikolas.garofil@uantwerpen.be>
|
||||
Nikolay Edigaryev <edigaryev@gmail.com>
|
||||
Nikolay Milovanov <nmil@itransformers.net>
|
||||
ningmingxiao <ning.mingxiao@zte.com.cn>
|
||||
Nirmal Mehta <nirmalkmehta@gmail.com>
|
||||
Nishant Totla <nishanttotla@gmail.com>
|
||||
NIWA Hideyuki <niwa.niwa@nifty.ne.jp>
|
||||
@@ -1615,6 +1629,7 @@ Omri Shiv <Omri.Shiv@teradata.com>
|
||||
Onur Filiz <onur.filiz@microsoft.com>
|
||||
Oriol Francès <oriolfa@gmail.com>
|
||||
Oscar Bonilla <6f6231@gmail.com>
|
||||
oscar.chen <2972789494@qq.com>
|
||||
Oskar Niburski <oskarniburski@gmail.com>
|
||||
Otto Kekäläinen <otto@seravo.fi>
|
||||
Ouyang Liduo <oyld0210@163.com>
|
||||
@@ -1822,6 +1837,7 @@ Rory Hunter <roryhunter2@gmail.com>
|
||||
Rory McCune <raesene@gmail.com>
|
||||
Ross Boucher <rboucher@gmail.com>
|
||||
Rovanion Luckey <rovanion.luckey@gmail.com>
|
||||
Roy Reznik <roy@wiz.io>
|
||||
Royce Remer <royceremer@gmail.com>
|
||||
Rozhnov Alexandr <nox73@ya.ru>
|
||||
Rudolph Gottesheim <r.gottesheim@loot.at>
|
||||
@@ -2271,6 +2287,7 @@ Xiaoyu Zhang <zhang.xiaoyu33@zte.com.cn>
|
||||
xichengliudui <1693291525@qq.com>
|
||||
xiekeyang <xiekeyang@huawei.com>
|
||||
Ximo Guanter Gonzálbez <joaquin.guantergonzalbez@telefonica.com>
|
||||
xin.li <xin.li@daocloud.io>
|
||||
Xinbo Weng <xihuanbo_0521@zju.edu.cn>
|
||||
Xinfeng Liu <xinfeng.liu@gmail.com>
|
||||
Xinzi Zhou <imdreamrunner@gmail.com>
|
||||
@@ -2282,6 +2299,7 @@ Yahya <ya7yaz@gmail.com>
|
||||
yalpul <yalpul@gmail.com>
|
||||
YAMADA Tsuyoshi <tyamada@minimum2scp.org>
|
||||
Yamasaki Masahide <masahide.y@gmail.com>
|
||||
Yamazaki Masashi <masi19bw@gmail.com>
|
||||
Yan Feng <yanfeng2@huawei.com>
|
||||
Yan Zhu <yanzhu@alauda.io>
|
||||
Yang Bai <hamo.by@gmail.com>
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package api // import "github.com/docker/docker/api"
|
||||
// Common constants for daemon and client.
|
||||
const (
|
||||
// DefaultVersion of Current REST API
|
||||
DefaultVersion = "1.42"
|
||||
DefaultVersion = "1.43"
|
||||
|
||||
// NoBaseImageSpecifier is the symbol used by the FROM
|
||||
// command to specify that no base image is to be used.
|
||||
|
||||
+71
-78
@@ -19,10 +19,10 @@ produces:
|
||||
consumes:
|
||||
- "application/json"
|
||||
- "text/plain"
|
||||
basePath: "/v1.42"
|
||||
basePath: "/v1.43"
|
||||
info:
|
||||
title: "Docker Engine API"
|
||||
version: "1.42"
|
||||
version: "1.43"
|
||||
x-logo:
|
||||
url: "https://docs.docker.com/assets/images/logo-docker-main.png"
|
||||
description: |
|
||||
@@ -55,8 +55,8 @@ info:
|
||||
the URL is not supported by the daemon, a HTTP `400 Bad Request` error message
|
||||
is returned.
|
||||
|
||||
If you omit the version-prefix, the current version of the API (v1.42) is used.
|
||||
For example, calling `/info` is the same as calling `/v1.42/info`. Using the
|
||||
If you omit the version-prefix, the current version of the API (v1.43) is used.
|
||||
For example, calling `/info` is the same as calling `/v1.43/info`. Using the
|
||||
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,
|
||||
@@ -976,6 +976,13 @@ definitions:
|
||||
items:
|
||||
type: "integer"
|
||||
minimum: 0
|
||||
Annotations:
|
||||
type: "object"
|
||||
description: |
|
||||
Arbitrary non-identifying metadata attached to container and
|
||||
provided to the runtime when the container is started.
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
|
||||
# Applicable to UNIX platforms
|
||||
CapAdd:
|
||||
@@ -1122,6 +1129,7 @@ definitions:
|
||||
remapping option is enabled.
|
||||
ShmSize:
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
description: |
|
||||
Size of `/dev/shm` in bytes. If omitted, the system uses 64MB.
|
||||
minimum: 0
|
||||
@@ -1610,6 +1618,34 @@ definitions:
|
||||
"WorkDir": "/var/lib/docker/overlay2/ef749362d13333e65fc95c572eb525abbe0052e16e086cb64bc3b98ae9aa6d74/work"
|
||||
}
|
||||
|
||||
FilesystemChange:
|
||||
description: |
|
||||
Change in the container's filesystem.
|
||||
type: "object"
|
||||
required: [Path, Kind]
|
||||
properties:
|
||||
Path:
|
||||
description: |
|
||||
Path to file or directory that has changed.
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
Kind:
|
||||
$ref: "#/definitions/ChangeType"
|
||||
|
||||
ChangeType:
|
||||
description: |
|
||||
Kind of change
|
||||
|
||||
Can be one of:
|
||||
|
||||
- `0`: Modified ("C")
|
||||
- `1`: Added ("A")
|
||||
- `2`: Deleted ("D")
|
||||
type: "integer"
|
||||
format: "uint8"
|
||||
enum: [0, 1, 2]
|
||||
x-nullable: false
|
||||
|
||||
ImageInspect:
|
||||
description: |
|
||||
Information about an image in the local image cache.
|
||||
@@ -1746,15 +1782,14 @@ definitions:
|
||||
Total size of the image including all layers it is composed of.
|
||||
|
||||
In versions of Docker before v1.10, this field was calculated from
|
||||
the image itself and all of its parent images. Docker v1.10 and up
|
||||
store images self-contained, and no longer use a parent-chain, making
|
||||
this field an equivalent of the Size field.
|
||||
the image itself and all of its parent images. Images are now stored
|
||||
self-contained, and no longer use a parent-chain, making this field
|
||||
an equivalent of the Size field.
|
||||
|
||||
This field is kept for backward compatibility, but may be removed in
|
||||
a future version of the API.
|
||||
> **Deprecated**: this field is kept for backward compatibility, but
|
||||
> will be removed in API v1.44.
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
x-nullable: false
|
||||
example: 1239828
|
||||
GraphDriver:
|
||||
$ref: "#/definitions/GraphDriverData"
|
||||
@@ -1802,7 +1837,6 @@ definitions:
|
||||
- Created
|
||||
- Size
|
||||
- SharedSize
|
||||
- VirtualSize
|
||||
- Labels
|
||||
- Containers
|
||||
properties:
|
||||
@@ -1888,19 +1922,17 @@ definitions:
|
||||
x-nullable: false
|
||||
example: 1239828
|
||||
VirtualSize:
|
||||
description: |
|
||||
description: |-
|
||||
Total size of the image including all layers it is composed of.
|
||||
|
||||
In versions of Docker before v1.10, this field was calculated from
|
||||
the image itself and all of its parent images. Docker v1.10 and up
|
||||
store images self-contained, and no longer use a parent-chain, making
|
||||
this field an equivalent of the Size field.
|
||||
the image itself and all of its parent images. Images are now stored
|
||||
self-contained, and no longer use a parent-chain, making this field
|
||||
an equivalent of the Size field.
|
||||
|
||||
This field is kept for backward compatibility, but may be removed in
|
||||
a future version of the API.
|
||||
Deprecated: this field is kept for backward compatibility, and will be removed in API v1.44.
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
x-nullable: false
|
||||
example: 172064416
|
||||
Labels:
|
||||
description: "User-defined key/value metadata."
|
||||
@@ -4652,7 +4684,8 @@ definitions:
|
||||
example: false
|
||||
OOMKilled:
|
||||
description: |
|
||||
Whether this container has been killed because it ran out of memory.
|
||||
Whether a process within this container has been killed because it ran
|
||||
out of memory since the container was last started.
|
||||
type: "boolean"
|
||||
example: false
|
||||
Dead:
|
||||
@@ -5035,7 +5068,7 @@ definitions:
|
||||
Go runtime (`GOOS`).
|
||||
|
||||
Currently returned values are "linux" and "windows". A full list of
|
||||
possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment).
|
||||
possible values can be found in the [Go documentation](https://go.dev/doc/install/source#environment).
|
||||
type: "string"
|
||||
example: "linux"
|
||||
Architecture:
|
||||
@@ -5043,7 +5076,7 @@ definitions:
|
||||
Hardware architecture of the host, as returned by the Go runtime
|
||||
(`GOARCH`).
|
||||
|
||||
A full list of possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment).
|
||||
A full list of possible values can be found in the [Go documentation](https://go.dev/doc/install/source#environment).
|
||||
type: "string"
|
||||
example: "x86_64"
|
||||
NCPU:
|
||||
@@ -5129,42 +5162,8 @@ definitions:
|
||||
ServerVersion:
|
||||
description: |
|
||||
Version string of the daemon.
|
||||
|
||||
> **Note**: the [standalone Swarm API](https://docs.docker.com/swarm/swarm-api/)
|
||||
> returns the Swarm version instead of the daemon version, for example
|
||||
> `swarm/1.2.8`.
|
||||
type: "string"
|
||||
example: "17.06.0-ce"
|
||||
ClusterStore:
|
||||
description: |
|
||||
URL of the distributed storage backend.
|
||||
|
||||
|
||||
The storage backend is used for multihost networking (to store
|
||||
network and endpoint information) and by the node discovery mechanism.
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
> **Deprecated**: This field is only propagated when using standalone Swarm
|
||||
> mode, and overlay networking using an external k/v store. Overlay
|
||||
> networks with Swarm mode enabled use the built-in raft store, and
|
||||
> this field will be empty.
|
||||
type: "string"
|
||||
example: "consul://consul.corp.example.com:8600/some/path"
|
||||
ClusterAdvertise:
|
||||
description: |
|
||||
The network endpoint that the Engine advertises for the purpose of
|
||||
node discovery. ClusterAdvertise is a `host:port` combination on which
|
||||
the daemon is reachable by other hosts.
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
> **Deprecated**: This field is only propagated when using standalone Swarm
|
||||
> mode, and overlay networking using an external k/v store. Overlay
|
||||
> networks with Swarm mode enabled use the built-in raft store, and
|
||||
> this field will be empty.
|
||||
type: "string"
|
||||
example: "node5.corp.example.com:8000"
|
||||
example: "24.0.2"
|
||||
Runtimes:
|
||||
description: |
|
||||
List of [OCI compliant](https://github.com/opencontainers/runtime-spec)
|
||||
@@ -5242,7 +5241,8 @@ definitions:
|
||||
SecurityOptions:
|
||||
description: |
|
||||
List of security features that are enabled on the daemon, such as
|
||||
apparmor, seccomp, SELinux, user-namespaces (userns), and rootless.
|
||||
apparmor, seccomp, SELinux, user-namespaces (userns), rootless and
|
||||
no-new-privileges.
|
||||
|
||||
Additional configuration options for each security feature may
|
||||
be present, and are included as a comma-separated list of key/value
|
||||
@@ -6875,9 +6875,9 @@ paths:
|
||||
Returns which files in a container's filesystem have been added, deleted,
|
||||
or modified. The `Kind` of modification can be one of:
|
||||
|
||||
- `0`: Modified
|
||||
- `1`: Added
|
||||
- `2`: Deleted
|
||||
- `0`: Modified ("C")
|
||||
- `1`: Added ("A")
|
||||
- `2`: Deleted ("D")
|
||||
operationId: "ContainerChanges"
|
||||
produces: ["application/json"]
|
||||
responses:
|
||||
@@ -6886,22 +6886,7 @@ paths:
|
||||
schema:
|
||||
type: "array"
|
||||
items:
|
||||
type: "object"
|
||||
x-go-name: "ContainerChangeResponseItem"
|
||||
title: "ContainerChangeResponseItem"
|
||||
description: "change item in response to ContainerChanges operation"
|
||||
required: [Path, Kind]
|
||||
properties:
|
||||
Path:
|
||||
description: "Path to file that has changed"
|
||||
type: "string"
|
||||
x-nullable: false
|
||||
Kind:
|
||||
description: "Kind of change"
|
||||
type: "integer"
|
||||
format: "uint8"
|
||||
enum: [0, 1, 2]
|
||||
x-nullable: false
|
||||
$ref: "#/definitions/FilesystemChange"
|
||||
examples:
|
||||
application/json:
|
||||
- Path: "/dev"
|
||||
@@ -8228,7 +8213,7 @@ paths:
|
||||
|
||||
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')
|
||||
- `until=<timestamp>` remove cache older than `<timestamp>`. The `<timestamp>` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon's local time.
|
||||
- `id=<id>`
|
||||
- `parent=<id>`
|
||||
- `type=<string>`
|
||||
@@ -9911,7 +9896,9 @@ paths:
|
||||
Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30"
|
||||
Warning: ""
|
||||
403:
|
||||
description: "operation not supported for pre-defined networks"
|
||||
description: |
|
||||
Forbidden operation. This happens when trying to create a network named after a pre-defined network,
|
||||
or when trying to create an overlay network on a daemon which is not part of a Swarm cluster.
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
404:
|
||||
@@ -10374,6 +10361,12 @@ paths:
|
||||
default if omitted.
|
||||
required: true
|
||||
type: "string"
|
||||
- name: "force"
|
||||
in: "query"
|
||||
description: |
|
||||
Force disable a plugin even if still in use.
|
||||
required: false
|
||||
type: "boolean"
|
||||
tags: ["Plugin"]
|
||||
/plugins/{name}/upgrade:
|
||||
post:
|
||||
|
||||
+5
-20
@@ -1,22 +1,7 @@
|
||||
package types // import "github.com/docker/docker/api/types"
|
||||
import "github.com/docker/docker/api/types/registry"
|
||||
|
||||
// AuthConfig contains authorization information for connecting to a Registry
|
||||
type AuthConfig struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Auth string `json:"auth,omitempty"`
|
||||
|
||||
// Email is an optional value associated with the username.
|
||||
// This field is deprecated and will be removed in a later
|
||||
// version of docker.
|
||||
Email string `json:"email,omitempty"`
|
||||
|
||||
ServerAddress string `json:"serveraddress,omitempty"`
|
||||
|
||||
// IdentityToken is used to authenticate the user and get
|
||||
// an access token for the registry.
|
||||
IdentityToken string `json:"identitytoken,omitempty"`
|
||||
|
||||
// RegistryToken is a bearer token to be sent to a registry
|
||||
RegistryToken string `json:"registrytoken,omitempty"`
|
||||
}
|
||||
// AuthConfig contains authorization information for connecting to a Registry.
|
||||
//
|
||||
// Deprecated: use github.com/docker/docker/api/types/registry.AuthConfig
|
||||
type AuthConfig = registry.AuthConfig
|
||||
+2
-1
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
@@ -180,7 +181,7 @@ type ImageBuildOptions struct {
|
||||
// at all (nil). See the parsing of buildArgs in
|
||||
// api/server/router/build/build_routes.go for even more info.
|
||||
BuildArgs map[string]*string
|
||||
AuthConfigs map[string]AuthConfig
|
||||
AuthConfigs map[string]registry.AuthConfig
|
||||
Context io.Reader
|
||||
Labels map[string]string
|
||||
// squash the resulting image's layers to the parent
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ package types // import "github.com/docker/docker/api/types"
|
||||
import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// configs holds structs used for internal communication between the
|
||||
@@ -16,7 +16,7 @@ type ContainerCreateConfig struct {
|
||||
Config *container.Config
|
||||
HostConfig *container.HostConfig
|
||||
NetworkingConfig *network.NetworkingConfig
|
||||
Platform *specs.Platform
|
||||
Platform *ocispec.Platform
|
||||
AdjustCPUShares bool
|
||||
}
|
||||
|
||||
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
package container
|
||||
|
||||
// ContainerChangeResponseItem change item in response to ContainerChanges operation
|
||||
//
|
||||
// Deprecated: use [FilesystemChange].
|
||||
type ContainerChangeResponseItem = FilesystemChange
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package container
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// ChangeType Kind of change
|
||||
//
|
||||
// Can be one of:
|
||||
//
|
||||
// - `0`: Modified ("C")
|
||||
// - `1`: Added ("A")
|
||||
// - `2`: Deleted ("D")
|
||||
//
|
||||
// swagger:model ChangeType
|
||||
type ChangeType uint8
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package container
|
||||
|
||||
const (
|
||||
// ChangeModify represents the modify operation.
|
||||
ChangeModify ChangeType = 0
|
||||
// ChangeAdd represents the add operation.
|
||||
ChangeAdd ChangeType = 1
|
||||
// ChangeDelete represents the delete operation.
|
||||
ChangeDelete ChangeType = 2
|
||||
)
|
||||
|
||||
func (ct ChangeType) String() string {
|
||||
switch ct {
|
||||
case ChangeModify:
|
||||
return "C"
|
||||
case ChangeAdd:
|
||||
return "A"
|
||||
case ChangeDelete:
|
||||
return "D"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Code generated by `swagger generate operation`. DO NOT EDIT.
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ContainerChangeResponseItem change item in response to ContainerChanges operation
|
||||
// swagger:model ContainerChangeResponseItem
|
||||
type ContainerChangeResponseItem struct {
|
||||
|
||||
// Kind of change
|
||||
// Required: true
|
||||
Kind uint8 `json:"Kind"`
|
||||
|
||||
// Path to file that has changed
|
||||
// Required: true
|
||||
Path string `json:"Path"`
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package container // import "github.com/docker/docker/api/types/container"
|
||||
|
||||
// ContainerCreateCreatedBody OK response to ContainerCreate operation
|
||||
//
|
||||
// Deprecated: use CreateResponse
|
||||
type ContainerCreateCreatedBody = CreateResponse
|
||||
|
||||
// ContainerWaitOKBody OK response to ContainerWait operation
|
||||
//
|
||||
// Deprecated: use WaitResponse
|
||||
type ContainerWaitOKBody = WaitResponse
|
||||
|
||||
// ContainerWaitOKBodyError container waiting error, if any
|
||||
//
|
||||
// Deprecated: use WaitExitError
|
||||
type ContainerWaitOKBodyError = WaitExitError
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package container
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// FilesystemChange Change in the container's filesystem.
|
||||
//
|
||||
// swagger:model FilesystemChange
|
||||
type FilesystemChange struct {
|
||||
|
||||
// kind
|
||||
// Required: true
|
||||
Kind ChangeType `json:"Kind"`
|
||||
|
||||
// Path to file or directory that has changed.
|
||||
//
|
||||
// Required: true
|
||||
Path string `json:"Path"`
|
||||
}
|
||||
Generated
Vendored
+60
-69
@@ -101,7 +101,8 @@ func (n IpcMode) IsShareable() bool {
|
||||
|
||||
// IsContainer indicates whether the container uses another container's ipc namespace.
|
||||
func (n IpcMode) IsContainer() bool {
|
||||
return strings.HasPrefix(string(n), string(IPCModeContainer)+":")
|
||||
_, ok := containerID(string(n))
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsNone indicates whether container IpcMode is set to "none".
|
||||
@@ -116,15 +117,14 @@ func (n IpcMode) IsEmpty() bool {
|
||||
|
||||
// Valid indicates whether the ipc mode is valid.
|
||||
func (n IpcMode) Valid() bool {
|
||||
// TODO(thaJeztah): align with PidMode, and consider container-mode without a container name/ID to be invalid.
|
||||
return n.IsEmpty() || n.IsNone() || n.IsPrivate() || n.IsHost() || n.IsShareable() || n.IsContainer()
|
||||
}
|
||||
|
||||
// Container returns the name of the container ipc stack is going to be used.
|
||||
func (n IpcMode) Container() string {
|
||||
if n.IsContainer() {
|
||||
return strings.TrimPrefix(string(n), string(IPCModeContainer)+":")
|
||||
}
|
||||
return ""
|
||||
func (n IpcMode) Container() (idOrName string) {
|
||||
idOrName, _ = containerID(string(n))
|
||||
return idOrName
|
||||
}
|
||||
|
||||
// NetworkMode represents the container network stack.
|
||||
@@ -147,17 +147,14 @@ func (n NetworkMode) IsPrivate() bool {
|
||||
|
||||
// IsContainer indicates whether container uses a container network stack.
|
||||
func (n NetworkMode) IsContainer() bool {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
return len(parts) > 1 && parts[0] == "container"
|
||||
_, ok := containerID(string(n))
|
||||
return ok
|
||||
}
|
||||
|
||||
// ConnectedContainer is the id of the container which network this container is connected to.
|
||||
func (n NetworkMode) ConnectedContainer() string {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
if len(parts) > 1 {
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
func (n NetworkMode) ConnectedContainer() (idOrName string) {
|
||||
idOrName, _ = containerID(string(n))
|
||||
return idOrName
|
||||
}
|
||||
|
||||
// UserDefined indicates user-created network
|
||||
@@ -178,18 +175,12 @@ func (n UsernsMode) IsHost() bool {
|
||||
|
||||
// IsPrivate indicates whether the container uses the a private userns.
|
||||
func (n UsernsMode) IsPrivate() bool {
|
||||
return !(n.IsHost())
|
||||
return !n.IsHost()
|
||||
}
|
||||
|
||||
// Valid indicates whether the userns is valid.
|
||||
func (n UsernsMode) Valid() bool {
|
||||
parts := strings.Split(string(n), ":")
|
||||
switch mode := parts[0]; mode {
|
||||
case "", "host":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return n == "" || n.IsHost()
|
||||
}
|
||||
|
||||
// CgroupSpec represents the cgroup to use for the container.
|
||||
@@ -197,22 +188,20 @@ type CgroupSpec string
|
||||
|
||||
// IsContainer indicates whether the container is using another container cgroup
|
||||
func (c CgroupSpec) IsContainer() bool {
|
||||
parts := strings.SplitN(string(c), ":", 2)
|
||||
return len(parts) > 1 && parts[0] == "container"
|
||||
_, ok := containerID(string(c))
|
||||
return ok
|
||||
}
|
||||
|
||||
// Valid indicates whether the cgroup spec is valid.
|
||||
func (c CgroupSpec) Valid() bool {
|
||||
return c.IsContainer() || c == ""
|
||||
// TODO(thaJeztah): align with PidMode, and consider container-mode without a container name/ID to be invalid.
|
||||
return c == "" || c.IsContainer()
|
||||
}
|
||||
|
||||
// Container returns the name of the container whose cgroup will be used.
|
||||
func (c CgroupSpec) Container() string {
|
||||
parts := strings.SplitN(string(c), ":", 2)
|
||||
if len(parts) > 1 {
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
// Container returns the ID or name of the container whose cgroup will be used.
|
||||
func (c CgroupSpec) Container() (idOrName string) {
|
||||
idOrName, _ = containerID(string(c))
|
||||
return idOrName
|
||||
}
|
||||
|
||||
// UTSMode represents the UTS namespace of the container.
|
||||
@@ -220,7 +209,7 @@ type UTSMode string
|
||||
|
||||
// IsPrivate indicates whether the container uses its private UTS namespace.
|
||||
func (n UTSMode) IsPrivate() bool {
|
||||
return !(n.IsHost())
|
||||
return !n.IsHost()
|
||||
}
|
||||
|
||||
// IsHost indicates whether the container uses the host's UTS namespace.
|
||||
@@ -230,13 +219,7 @@ func (n UTSMode) IsHost() bool {
|
||||
|
||||
// Valid indicates whether the UTS namespace is valid.
|
||||
func (n UTSMode) Valid() bool {
|
||||
parts := strings.Split(string(n), ":")
|
||||
switch mode := parts[0]; mode {
|
||||
case "", "host":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return n == "" || n.IsHost()
|
||||
}
|
||||
|
||||
// PidMode represents the pid namespace of the container.
|
||||
@@ -254,32 +237,19 @@ func (n PidMode) IsHost() bool {
|
||||
|
||||
// IsContainer indicates whether the container uses a container's pid namespace.
|
||||
func (n PidMode) IsContainer() bool {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
return len(parts) > 1 && parts[0] == "container"
|
||||
_, ok := containerID(string(n))
|
||||
return ok
|
||||
}
|
||||
|
||||
// Valid indicates whether the pid namespace is valid.
|
||||
func (n PidMode) Valid() bool {
|
||||
parts := strings.Split(string(n), ":")
|
||||
switch mode := parts[0]; mode {
|
||||
case "", "host":
|
||||
case "container":
|
||||
if len(parts) != 2 || parts[1] == "" {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return n == "" || n.IsHost() || validContainer(string(n))
|
||||
}
|
||||
|
||||
// Container returns the name of the container whose pid namespace is going to be used.
|
||||
func (n PidMode) Container() string {
|
||||
parts := strings.SplitN(string(n), ":", 2)
|
||||
if len(parts) > 1 {
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
func (n PidMode) Container() (idOrName string) {
|
||||
idOrName, _ = containerID(string(n))
|
||||
return idOrName
|
||||
}
|
||||
|
||||
// DeviceRequest represents a request for devices from a device driver.
|
||||
@@ -408,16 +378,17 @@ type UpdateConfig struct {
|
||||
// Portable information *should* appear in Config.
|
||||
type HostConfig struct {
|
||||
// Applicable to all platforms
|
||||
Binds []string // List of volume bindings for this container
|
||||
ContainerIDFile string // File (path) where the containerId is written
|
||||
LogConfig LogConfig // Configuration of the logs for this container
|
||||
NetworkMode NetworkMode // Network mode to use for the container
|
||||
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
|
||||
RestartPolicy RestartPolicy // Restart policy to be used for the container
|
||||
AutoRemove bool // Automatically remove container when it exits
|
||||
VolumeDriver string // Name of the volume driver used to mount volumes
|
||||
VolumesFrom []string // List of volumes to take from other container
|
||||
ConsoleSize [2]uint // Initial console size (height,width)
|
||||
Binds []string // List of volume bindings for this container
|
||||
ContainerIDFile string // File (path) where the containerId is written
|
||||
LogConfig LogConfig // Configuration of the logs for this container
|
||||
NetworkMode NetworkMode // Network mode to use for the container
|
||||
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
|
||||
RestartPolicy RestartPolicy // Restart policy to be used for the container
|
||||
AutoRemove bool // Automatically remove container when it exits
|
||||
VolumeDriver string // Name of the volume driver used to mount volumes
|
||||
VolumesFrom []string // List of volumes to take from other container
|
||||
ConsoleSize [2]uint // Initial console size (height,width)
|
||||
Annotations map[string]string `json:",omitempty"` // Arbitrary non-identifying metadata attached to container and provided to the runtime
|
||||
|
||||
// Applicable to UNIX platforms
|
||||
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
|
||||
@@ -463,3 +434,23 @@ type HostConfig struct {
|
||||
// Run a custom init inside the container, if null, use the daemon's configured settings
|
||||
Init *bool `json:",omitempty"`
|
||||
}
|
||||
|
||||
// containerID splits "container:<ID|name>" values. It returns the container
|
||||
// ID or name, and whether an ID/name was found. It returns an empty string and
|
||||
// a "false" if the value does not have a "container:" prefix. Further validation
|
||||
// of the returned, including checking if the value is empty, should be handled
|
||||
// by the caller.
|
||||
func containerID(val string) (idOrName string, ok bool) {
|
||||
k, v, hasSep := strings.Cut(val, ":")
|
||||
if !hasSep || k != "container" {
|
||||
return "", false
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
|
||||
// validContainer checks if the given value is a "container:" mode with
|
||||
// a non-empty name/ID.
|
||||
func validContainer(val string) bool {
|
||||
id, ok := containerID(val)
|
||||
return ok && id != ""
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
package types // import "github.com/docker/docker/api/types"
|
||||
|
||||
import "github.com/docker/docker/api/types/volume"
|
||||
|
||||
// Volume volume
|
||||
//
|
||||
// Deprecated: use github.com/docker/docker/api/types/volume.Volume
|
||||
type Volume = volume.Volume
|
||||
|
||||
// VolumeUsageData Usage details about the volume. This information is used by the
|
||||
// `GET /system/df` endpoint, and omitted in other endpoints.
|
||||
//
|
||||
// Deprecated: use github.com/docker/docker/api/types/volume.UsageData
|
||||
type VolumeUsageData = volume.UsageData
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package filters
|
||||
|
||||
import "fmt"
|
||||
|
||||
// invalidFilter indicates that the provided filter or its value is invalid
|
||||
type invalidFilter struct {
|
||||
Filter string
|
||||
Value []string
|
||||
}
|
||||
|
||||
func (e invalidFilter) Error() string {
|
||||
msg := "invalid filter"
|
||||
if e.Filter != "" {
|
||||
msg += " '" + e.Filter
|
||||
if e.Value != nil {
|
||||
msg = fmt.Sprintf("%s=%s", msg, e.Value)
|
||||
}
|
||||
msg += "'"
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// InvalidParameter marks this error as ErrInvalidParameter
|
||||
func (e invalidFilter) InvalidParameter() {}
|
||||
|
||||
// unreachableCode is an error indicating that the code path was not expected to be reached.
|
||||
type unreachableCode struct {
|
||||
Filter string
|
||||
Value []string
|
||||
}
|
||||
|
||||
// System marks this error as ErrSystem
|
||||
func (e unreachableCode) System() {}
|
||||
|
||||
func (e unreachableCode) Error() string {
|
||||
return fmt.Sprintf("unreachable code reached for filter: %q with values: %s", e.Filter, e.Value)
|
||||
}
|
||||
+37
-14
@@ -10,7 +10,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Args stores a mapping of keys to a set of multiple values.
|
||||
@@ -99,7 +98,7 @@ func FromJSON(p string) (Args, error) {
|
||||
// Fallback to parsing arguments in the legacy slice format
|
||||
deprecated := map[string][]string{}
|
||||
if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil {
|
||||
return args, invalidFilter{errors.Wrap(err, "invalid filter")}
|
||||
return args, &invalidFilter{}
|
||||
}
|
||||
|
||||
args.fields = deprecatedArgs(deprecated)
|
||||
@@ -163,13 +162,13 @@ func (args Args) MatchKVList(key string, sources map[string]string) bool {
|
||||
}
|
||||
|
||||
for value := range fieldValues {
|
||||
testKV := strings.SplitN(value, "=", 2)
|
||||
testK, testV, hasValue := strings.Cut(value, "=")
|
||||
|
||||
v, ok := sources[testKV[0]]
|
||||
v, ok := sources[testK]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if len(testKV) == 2 && testKV[1] != v {
|
||||
if hasValue && testV != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -196,6 +195,38 @@ func (args Args) Match(field, source string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetBoolOrDefault returns a boolean value of the key if the key is present
|
||||
// and is intepretable as a boolean value. Otherwise the default value is returned.
|
||||
// Error is not nil only if the filter values are not valid boolean or are conflicting.
|
||||
func (args Args) GetBoolOrDefault(key string, defaultValue bool) (bool, error) {
|
||||
fieldValues, ok := args.fields[key]
|
||||
|
||||
if !ok {
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
if len(fieldValues) == 0 {
|
||||
return defaultValue, &invalidFilter{key, nil}
|
||||
}
|
||||
|
||||
isFalse := fieldValues["0"] || fieldValues["false"]
|
||||
isTrue := fieldValues["1"] || fieldValues["true"]
|
||||
|
||||
conflicting := isFalse && isTrue
|
||||
invalid := !isFalse && !isTrue
|
||||
|
||||
if conflicting || invalid {
|
||||
return defaultValue, &invalidFilter{key, args.Get(key)}
|
||||
} else if isFalse {
|
||||
return false, nil
|
||||
} else if isTrue {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// This code shouldn't be reached.
|
||||
return defaultValue, &unreachableCode{Filter: key, Value: args.Get(key)}
|
||||
}
|
||||
|
||||
// ExactMatch returns true if the source matches exactly one of the values.
|
||||
func (args Args) ExactMatch(key, source string) bool {
|
||||
fieldValues, ok := args.fields[key]
|
||||
@@ -246,20 +277,12 @@ func (args Args) Contains(field string) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
type invalidFilter struct{ error }
|
||||
|
||||
func (e invalidFilter) Error() string {
|
||||
return e.error.Error()
|
||||
}
|
||||
|
||||
func (invalidFilter) InvalidParameter() {}
|
||||
|
||||
// Validate compared the set of accepted keys against the keys in the mapping.
|
||||
// An error is returned if any mapping keys are not in the accepted set.
|
||||
func (args Args) Validate(accepted map[string]bool) error {
|
||||
for name := range args.fields {
|
||||
if !accepted[name] {
|
||||
return invalidFilter{errors.New("invalid filter '" + name + "'")}
|
||||
return &invalidFilter{name, nil}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package image
|
||||
|
||||
import ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
|
||||
// GetImageOpts holds parameters to inspect an image.
|
||||
type GetImageOpts struct {
|
||||
Platform *ocispec.Platform
|
||||
Details bool
|
||||
}
|
||||
+5
-8
@@ -85,13 +85,10 @@ type ImageSummary struct {
|
||||
// Total size of the image including all layers it is composed of.
|
||||
//
|
||||
// In versions of Docker before v1.10, this field was calculated from
|
||||
// the image itself and all of its parent images. Docker v1.10 and up
|
||||
// store images self-contained, and no longer use a parent-chain, making
|
||||
// this field an equivalent of the Size field.
|
||||
// the image itself and all of its parent images. Images are now stored
|
||||
// self-contained, and no longer use a parent-chain, making this field
|
||||
// an equivalent of the Size field.
|
||||
//
|
||||
// This field is kept for backward compatibility, but may be removed in
|
||||
// a future version of the API.
|
||||
//
|
||||
// Required: true
|
||||
VirtualSize int64 `json:"VirtualSize"`
|
||||
// Deprecated: this field is kept for backward compatibility, and will be removed in API v1.44.
|
||||
VirtualSize int64 `json:"VirtualSize,omitempty"`
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package registry // import "github.com/docker/docker/api/types/registry"
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// AuthHeader is the name of the header used to send encoded registry
|
||||
// authorization credentials for registry operations (push/pull).
|
||||
const AuthHeader = "X-Registry-Auth"
|
||||
|
||||
// AuthConfig contains authorization information for connecting to a Registry.
|
||||
type AuthConfig struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Auth string `json:"auth,omitempty"`
|
||||
|
||||
// Email is an optional value associated with the username.
|
||||
// This field is deprecated and will be removed in a later
|
||||
// version of docker.
|
||||
Email string `json:"email,omitempty"`
|
||||
|
||||
ServerAddress string `json:"serveraddress,omitempty"`
|
||||
|
||||
// IdentityToken is used to authenticate the user and get
|
||||
// an access token for the registry.
|
||||
IdentityToken string `json:"identitytoken,omitempty"`
|
||||
|
||||
// RegistryToken is a bearer token to be sent to a registry
|
||||
RegistryToken string `json:"registrytoken,omitempty"`
|
||||
}
|
||||
|
||||
// EncodeAuthConfig serializes the auth configuration as a base64url encoded
|
||||
// RFC4648, section 5) JSON string for sending through the X-Registry-Auth header.
|
||||
//
|
||||
// For details on base64url encoding, see:
|
||||
// - RFC4648, section 5: https://tools.ietf.org/html/rfc4648#section-5
|
||||
func EncodeAuthConfig(authConfig AuthConfig) (string, error) {
|
||||
buf, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return "", errInvalidParameter{err}
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(buf), nil
|
||||
}
|
||||
|
||||
// DecodeAuthConfig decodes base64url encoded (RFC4648, section 5) JSON
|
||||
// authentication information as sent through the X-Registry-Auth header.
|
||||
//
|
||||
// This function always returns an AuthConfig, even if an error occurs. It is up
|
||||
// to the caller to decide if authentication is required, and if the error can
|
||||
// be ignored.
|
||||
//
|
||||
// For details on base64url encoding, see:
|
||||
// - RFC4648, section 5: https://tools.ietf.org/html/rfc4648#section-5
|
||||
func DecodeAuthConfig(authEncoded string) (*AuthConfig, error) {
|
||||
if authEncoded == "" {
|
||||
return &AuthConfig{}, nil
|
||||
}
|
||||
|
||||
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
return decodeAuthConfigFromReader(authJSON)
|
||||
}
|
||||
|
||||
// DecodeAuthConfigBody decodes authentication information as sent as JSON in the
|
||||
// body of a request. This function is to provide backward compatibility with old
|
||||
// clients and API versions. Current clients and API versions expect authentication
|
||||
// to be provided through the X-Registry-Auth header.
|
||||
//
|
||||
// Like DecodeAuthConfig, this function always returns an AuthConfig, even if an
|
||||
// error occurs. It is up to the caller to decide if authentication is required,
|
||||
// and if the error can be ignored.
|
||||
func DecodeAuthConfigBody(rdr io.ReadCloser) (*AuthConfig, error) {
|
||||
return decodeAuthConfigFromReader(rdr)
|
||||
}
|
||||
|
||||
func decodeAuthConfigFromReader(rdr io.Reader) (*AuthConfig, error) {
|
||||
authConfig := &AuthConfig{}
|
||||
if err := json.NewDecoder(rdr).Decode(authConfig); err != nil {
|
||||
// always return an (empty) AuthConfig to increase compatibility with
|
||||
// the existing API.
|
||||
return &AuthConfig{}, invalid(err)
|
||||
}
|
||||
return authConfig, nil
|
||||
}
|
||||
|
||||
func invalid(err error) error {
|
||||
return errInvalidParameter{errors.Wrap(err, "invalid X-Registry-Auth header")}
|
||||
}
|
||||
|
||||
type errInvalidParameter struct{ error }
|
||||
|
||||
func (errInvalidParameter) InvalidParameter() {}
|
||||
|
||||
func (e errInvalidParameter) Cause() error { return e.error }
|
||||
|
||||
func (e errInvalidParameter) Unwrap() error { return e.error }
|
||||
+3
-3
@@ -4,7 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// ServiceConfig stores daemon registry services configuration.
|
||||
@@ -113,8 +113,8 @@ type SearchResults struct {
|
||||
type DistributionInspect struct {
|
||||
// Descriptor contains information about the manifest, including
|
||||
// the content addressable digest
|
||||
Descriptor v1.Descriptor
|
||||
Descriptor ocispec.Descriptor
|
||||
// Platforms contains the list of platforms supported by the image,
|
||||
// obtained by parsing the manifest
|
||||
Platforms []v1.Platform
|
||||
Platforms []ocispec.Platform
|
||||
}
|
||||
+20
-20
@@ -95,37 +95,37 @@ func GetTimestamp(value string, reference time.Time) (string, error) {
|
||||
return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil
|
||||
}
|
||||
|
||||
// ParseTimestamps returns seconds and nanoseconds from a timestamp that has the
|
||||
// format "%d.%09d", time.Unix(), int64(time.Nanosecond()))
|
||||
// if the incoming nanosecond portion is longer or shorter than 9 digits it is
|
||||
// converted to nanoseconds. The expectation is that the seconds and
|
||||
// seconds will be used to create a time variable. For example:
|
||||
// ParseTimestamps returns seconds and nanoseconds from a timestamp that has
|
||||
// the format ("%d.%09d", time.Unix(), int64(time.Nanosecond())).
|
||||
// If the incoming nanosecond portion is longer than 9 digits it is truncated.
|
||||
// The expectation is that the seconds and nanoseconds will be used to create a
|
||||
// time variable. For example:
|
||||
//
|
||||
// seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0)
|
||||
// if err == nil since := time.Unix(seconds, nanoseconds)
|
||||
// seconds, nanoseconds, _ := ParseTimestamp("1136073600.000000001",0)
|
||||
// since := time.Unix(seconds, nanoseconds)
|
||||
//
|
||||
// returns seconds as def(aultSeconds) if value == ""
|
||||
func ParseTimestamps(value string, def int64) (int64, int64, error) {
|
||||
// returns seconds as defaultSeconds if value == ""
|
||||
func ParseTimestamps(value string, defaultSeconds int64) (seconds int64, nanoseconds int64, err error) {
|
||||
if value == "" {
|
||||
return def, 0, nil
|
||||
return defaultSeconds, 0, nil
|
||||
}
|
||||
return parseTimestamp(value)
|
||||
}
|
||||
|
||||
func parseTimestamp(value string) (int64, int64, error) {
|
||||
sa := strings.SplitN(value, ".", 2)
|
||||
s, err := strconv.ParseInt(sa[0], 10, 64)
|
||||
func parseTimestamp(value string) (sec int64, nsec int64, err error) {
|
||||
s, n, ok := strings.Cut(value, ".")
|
||||
sec, err = strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return s, 0, err
|
||||
return sec, 0, err
|
||||
}
|
||||
if len(sa) != 2 {
|
||||
return s, 0, nil
|
||||
if !ok {
|
||||
return sec, 0, nil
|
||||
}
|
||||
n, err := strconv.ParseInt(sa[1], 10, 64)
|
||||
nsec, err = strconv.ParseInt(n, 10, 64)
|
||||
if err != nil {
|
||||
return s, n, err
|
||||
return sec, nsec, err
|
||||
}
|
||||
// should already be in nanoseconds but just in case convert n to nanoseconds
|
||||
n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1]))))
|
||||
return s, n, nil
|
||||
nsec = int64(float64(nsec) * math.Pow(float64(10), float64(9-len(n))))
|
||||
return sec, nsec, nil
|
||||
}
|
||||
+18
-16
@@ -123,9 +123,8 @@ type ImageInspect struct {
|
||||
// store images self-contained, and no longer use a parent-chain, making
|
||||
// this field an equivalent of the Size field.
|
||||
//
|
||||
// This field is kept for backward compatibility, but may be removed in
|
||||
// a future version of the API.
|
||||
VirtualSize int64 // TODO(thaJeztah): deprecate this field
|
||||
// Deprecated: Unused in API 1.43 and up, but kept for backward compatibility with older API versions.
|
||||
VirtualSize int64 `json:"VirtualSize,omitempty"`
|
||||
|
||||
// GraphDriver holds information about the storage driver used to store the
|
||||
// container's and image's filesystem.
|
||||
@@ -297,8 +296,6 @@ type Info struct {
|
||||
Labels []string
|
||||
ExperimentalBuild bool
|
||||
ServerVersion string
|
||||
ClusterStore string `json:",omitempty"` // Deprecated: host-discovery and overlay networks with external k/v stores are deprecated
|
||||
ClusterAdvertise string `json:",omitempty"` // Deprecated: host-discovery and overlay networks with external k/v stores are deprecated
|
||||
Runtimes map[string]Runtime
|
||||
DefaultRuntime string
|
||||
Swarm swarm.Info
|
||||
@@ -350,20 +347,19 @@ func DecodeSecurityOptions(opts []string) ([]SecurityOpt, error) {
|
||||
continue
|
||||
}
|
||||
secopt := SecurityOpt{}
|
||||
split := strings.Split(opt, ",")
|
||||
for _, s := range split {
|
||||
kv := strings.SplitN(s, "=", 2)
|
||||
if len(kv) != 2 {
|
||||
for _, s := range strings.Split(opt, ",") {
|
||||
k, v, ok := strings.Cut(s, "=")
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid security option %q", s)
|
||||
}
|
||||
if kv[0] == "" || kv[1] == "" {
|
||||
if k == "" || v == "" {
|
||||
return nil, errors.New("invalid empty security option")
|
||||
}
|
||||
if kv[0] == "name" {
|
||||
secopt.Name = kv[1]
|
||||
if k == "name" {
|
||||
secopt.Name = v
|
||||
continue
|
||||
}
|
||||
secopt.Options = append(secopt.Options, KeyValue{Key: kv[0], Value: kv[1]})
|
||||
secopt.Options = append(secopt.Options, KeyValue{Key: k, Value: v})
|
||||
}
|
||||
so = append(so, secopt)
|
||||
}
|
||||
@@ -656,12 +652,18 @@ type Checkpoint struct {
|
||||
|
||||
// Runtime describes an OCI runtime
|
||||
type Runtime struct {
|
||||
Path string `json:"path"`
|
||||
// "Legacy" runtime configuration for runc-compatible runtimes.
|
||||
|
||||
Path string `json:"path,omitempty"`
|
||||
Args []string `json:"runtimeArgs,omitempty"`
|
||||
|
||||
// Shimv2 runtime configuration. Mutually exclusive with the legacy config above.
|
||||
|
||||
Type string `json:"runtimeType,omitempty"`
|
||||
Options map[string]interface{} `json:"options,omitempty"`
|
||||
|
||||
// This is exposed here only for internal use
|
||||
// It is not currently supported to specify custom shim configs
|
||||
Shim *ShimConfig `json:"-"`
|
||||
ShimConfig *ShimConfig `json:"-"`
|
||||
}
|
||||
|
||||
// ShimConfig is used by runtime to configure containerd shims
|
||||
|
||||
+4
-4
@@ -16,11 +16,11 @@ func compare(v1, v2 string) int {
|
||||
otherTab = strings.Split(v2, ".")
|
||||
)
|
||||
|
||||
max := len(currTab)
|
||||
if len(otherTab) > max {
|
||||
max = len(otherTab)
|
||||
maxVer := len(currTab)
|
||||
if len(otherTab) > maxVer {
|
||||
maxVer = len(otherTab)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
for i := 0; i < maxVer; i++ {
|
||||
var currInt, otherInt int
|
||||
|
||||
if len(currTab) > i {
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
package volume // import "github.com/docker/docker/api/types/volume"
|
||||
|
||||
// VolumeCreateBody Volume configuration
|
||||
//
|
||||
// Deprecated: use CreateOptions
|
||||
type VolumeCreateBody = CreateOptions
|
||||
|
||||
// VolumeListOKBody Volume list response
|
||||
//
|
||||
// Deprecated: use ListResponse
|
||||
type VolumeListOKBody = ListResponse
|
||||
+5
-5
@@ -3,8 +3,8 @@ package client // import "github.com/docker/docker/client"
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
@@ -23,12 +23,12 @@ func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePru
|
||||
if opts.All {
|
||||
query.Set("all", "1")
|
||||
}
|
||||
query.Set("keep-storage", fmt.Sprintf("%d", opts.KeepStorage))
|
||||
filters, err := filters.ToJSON(opts.Filters)
|
||||
query.Set("keep-storage", strconv.Itoa(int(opts.KeepStorage)))
|
||||
f, err := filters.ToJSON(opts.Filters)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "prune could not marshal filters option")
|
||||
}
|
||||
query.Set("filters", filters)
|
||||
query.Set("filters", f)
|
||||
|
||||
serverResp, err := cli.post(ctx, "/build/prune", query, nil, nil)
|
||||
defer ensureReaderClosed(serverResp)
|
||||
@@ -38,7 +38,7 @@ func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePru
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil {
|
||||
return nil, fmt.Errorf("Error retrieving disk usage: %v", err)
|
||||
return nil, errors.Wrap(err, "error retrieving disk usage")
|
||||
}
|
||||
|
||||
return &report, nil
|
||||
|
||||
+47
-13
@@ -6,9 +6,10 @@ https://docs.docker.com/engine/api/
|
||||
|
||||
# Usage
|
||||
|
||||
You use the library by creating a client object and calling methods on it. The
|
||||
client can be created either from environment variables with NewClientWithOpts(client.FromEnv),
|
||||
or configured manually with NewClient().
|
||||
You use the library by constructing a client object using [NewClientWithOpts]
|
||||
and calling methods on it. The client can be configured from environment
|
||||
variables by passing the [FromEnv] option, or configured manually by passing any
|
||||
of the other available [Opts].
|
||||
|
||||
For example, to list running containers (the equivalent of "docker ps"):
|
||||
|
||||
@@ -55,6 +56,36 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// DummyHost is a hostname used for local communication.
|
||||
//
|
||||
// It acts as a valid formatted hostname for local connections (such as "unix://"
|
||||
// or "npipe://") which do not require a hostname. It should never be resolved,
|
||||
// but uses the special-purpose ".localhost" TLD (as defined in [RFC 2606, Section 2]
|
||||
// and [RFC 6761, Section 6.3]).
|
||||
//
|
||||
// [RFC 7230, Section 5.4] defines that an empty header must be used for such
|
||||
// cases:
|
||||
//
|
||||
// If the authority component is missing or undefined for the target URI,
|
||||
// then a client MUST send a Host header field with an empty field-value.
|
||||
//
|
||||
// However, [Go stdlib] enforces the semantics of HTTP(S) over TCP, does not
|
||||
// allow an empty header to be used, and requires req.URL.Scheme to be either
|
||||
// "http" or "https".
|
||||
//
|
||||
// For further details, refer to:
|
||||
//
|
||||
// - https://github.com/docker/engine-api/issues/189
|
||||
// - https://github.com/golang/go/issues/13624
|
||||
// - https://github.com/golang/go/issues/61076
|
||||
// - https://github.com/moby/moby/issues/45935
|
||||
//
|
||||
// [RFC 2606, Section 2]: https://www.rfc-editor.org/rfc/rfc2606.html#section-2
|
||||
// [RFC 6761, Section 6.3]: https://www.rfc-editor.org/rfc/rfc6761#section-6.3
|
||||
// [RFC 7230, Section 5.4]: https://datatracker.ietf.org/doc/html/rfc7230#section-5.4
|
||||
// [Go stdlib]: https://github.com/golang/go/blob/6244b1946bc2101b01955468f1be502dbadd6807/src/net/http/transport.go#L558-L569
|
||||
const DummyHost = "api.moby.localhost"
|
||||
|
||||
// ErrRedirect is the error returned by checkRedirect when the request is non-GET.
|
||||
var ErrRedirect = errors.New("unexpected redirect in response")
|
||||
|
||||
@@ -125,7 +156,12 @@ func CheckRedirect(req *http.Request, via []*http.Request) error {
|
||||
// client.WithAPIVersionNegotiation(),
|
||||
// )
|
||||
func NewClientWithOpts(ops ...Opt) (*Client, error) {
|
||||
client, err := defaultHTTPClient(DefaultDockerHost)
|
||||
hostURL, err := ParseHostURL(DefaultDockerHost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := defaultHTTPClient(hostURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -133,8 +169,8 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) {
|
||||
host: DefaultDockerHost,
|
||||
version: api.DefaultVersion,
|
||||
client: client,
|
||||
proto: defaultProto,
|
||||
addr: defaultAddr,
|
||||
proto: hostURL.Scheme,
|
||||
addr: hostURL.Host,
|
||||
}
|
||||
|
||||
for _, op := range ops {
|
||||
@@ -160,13 +196,12 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func defaultHTTPClient(host string) (*http.Client, error) {
|
||||
hostURL, err := ParseHostURL(host)
|
||||
func defaultHTTPClient(hostURL *url.URL) (*http.Client, error) {
|
||||
transport := &http.Transport{}
|
||||
err := sockets.ConfigureTransport(transport, hostURL.Scheme, hostURL.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transport := &http.Transport{}
|
||||
_ = sockets.ConfigureTransport(transport, hostURL.Scheme, hostURL.Host)
|
||||
return &http.Client{
|
||||
Transport: transport,
|
||||
CheckRedirect: CheckRedirect,
|
||||
@@ -282,13 +317,12 @@ func (cli *Client) HTTPClient() *http.Client {
|
||||
// ParseHostURL parses a url string, validates the string is a host url, and
|
||||
// returns the parsed URL
|
||||
func ParseHostURL(host string) (*url.URL, error) {
|
||||
protoAddrParts := strings.SplitN(host, "://", 2)
|
||||
if len(protoAddrParts) == 1 {
|
||||
proto, addr, ok := strings.Cut(host, "://")
|
||||
if !ok || addr == "" {
|
||||
return nil, errors.Errorf("unable to parse docker host `%s`", host)
|
||||
}
|
||||
|
||||
var basePath string
|
||||
proto, addr := protoAddrParts[0], protoAddrParts[1]
|
||||
if proto == "tcp" {
|
||||
parsed, err := url.Parse("tcp://" + addr)
|
||||
if err != nil {
|
||||
|
||||
+6
-2
@@ -9,7 +9,11 @@ import "net/http"
|
||||
// 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
|
||||
//
|
||||
// Deprecated: use [NewClientWithOpts] passing the [WithHost], [WithVersion],
|
||||
// [WithHTTPClient] and [WithHTTPHeaders] options. We recommend enabling API
|
||||
// version negotiation by passing the [WithAPIVersionNegotiation] option instead
|
||||
// of WithVersion.
|
||||
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))
|
||||
}
|
||||
@@ -17,7 +21,7 @@ func NewClient(host string, version string, client *http.Client, httpHeaders map
|
||||
// NewEnvClient initializes a new API client based on environment variables.
|
||||
// See FromEnv for a list of support environment variables.
|
||||
//
|
||||
// Deprecated: use NewClientWithOpts(FromEnv)
|
||||
// Deprecated: use [NewClientWithOpts] passing the [FromEnv] option.
|
||||
func NewEnvClient() (*Client, error) {
|
||||
return NewClientWithOpts(FromEnv)
|
||||
}
|
||||
+2
-5
@@ -1,11 +1,8 @@
|
||||
//go:build linux || freebsd || openbsd || netbsd || darwin || solaris || illumos || dragonfly
|
||||
// +build linux freebsd openbsd netbsd darwin solaris illumos dragonfly
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package client // import "github.com/docker/docker/client"
|
||||
|
||||
// DefaultDockerHost defines OS-specific default host if the DOCKER_HOST
|
||||
// (EnvOverrideHost) environment variable is unset or empty.
|
||||
const DefaultDockerHost = "unix:///var/run/docker.sock"
|
||||
|
||||
const defaultProto = "unix"
|
||||
const defaultAddr = "/var/run/docker.sock"
|
||||
-3
@@ -3,6 +3,3 @@ package client // import "github.com/docker/docker/client"
|
||||
// DefaultDockerHost defines OS-specific default host if the DOCKER_HOST
|
||||
// (EnvOverrideHost) environment variable is unset or empty.
|
||||
const DefaultDockerHost = "npipe:////./pipe/docker_engine"
|
||||
|
||||
const defaultProto = "npipe"
|
||||
const defaultAddr = "//./pipe/docker_engine"
|
||||
+3
-3
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
type configWrapper struct {
|
||||
@@ -20,7 +20,7 @@ type configWrapper struct {
|
||||
|
||||
// ContainerCreate creates a new container based on the given configuration.
|
||||
// It can be associated with a name, but it's not mandatory.
|
||||
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.CreateResponse, error) {
|
||||
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) {
|
||||
var response container.CreateResponse
|
||||
|
||||
if err := cli.NewVersionError("1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil {
|
||||
@@ -75,7 +75,7 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
|
||||
// Similar to containerd's platforms.Format(), but does allow components to be
|
||||
// omitted (e.g. pass "architecture" only, without "os":
|
||||
// https://github.com/containerd/containerd/blob/v1.5.2/platforms/platforms.go#L243-L263
|
||||
func formatPlatform(platform *specs.Platform) string {
|
||||
func formatPlatform(platform *ocispec.Platform) string {
|
||||
if platform == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@ import (
|
||||
)
|
||||
|
||||
// ContainerDiff shows differences in a container filesystem since it was started.
|
||||
func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]container.ContainerChangeResponseItem, error) {
|
||||
var changes []container.ContainerChangeResponseItem
|
||||
func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]container.FilesystemChange, error) {
|
||||
var changes []container.FilesystemChange
|
||||
|
||||
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil)
|
||||
defer ensureReaderClosed(serverResp)
|
||||
|
||||
+4
-4
@@ -5,13 +5,13 @@ import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
)
|
||||
|
||||
// DistributionInspect returns the image digest with the full manifest.
|
||||
func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registrytypes.DistributionInspect, error) {
|
||||
func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registry.DistributionInspect, error) {
|
||||
// Contact the registry to retrieve digest and platform information
|
||||
var distributionInspect registrytypes.DistributionInspect
|
||||
var distributionInspect registry.DistributionInspect
|
||||
if image == "" {
|
||||
return distributionInspect, objectNotFoundError{object: "distribution", id: image}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegist
|
||||
|
||||
if encodedRegistryAuth != "" {
|
||||
headers = map[string][]string{
|
||||
"X-Registry-Auth": {encodedRegistryAuth},
|
||||
registry.AuthHeader: {encodedRegistryAuth},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-25
@@ -58,31 +58,6 @@ func (e objectNotFoundError) Error() string {
|
||||
return fmt.Sprintf("Error: No such %s: %s", e.object, e.id)
|
||||
}
|
||||
|
||||
// IsErrUnauthorized returns true if the error is caused
|
||||
// when a remote registry authentication fails
|
||||
//
|
||||
// Deprecated: use errdefs.IsUnauthorized
|
||||
func IsErrUnauthorized(err error) bool {
|
||||
return errdefs.IsUnauthorized(err)
|
||||
}
|
||||
|
||||
type pluginPermissionDenied struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (e pluginPermissionDenied) Error() string {
|
||||
return "Permission denied while installing plugin " + e.name
|
||||
}
|
||||
|
||||
// IsErrNotImplemented returns true if the error is a NotImplemented error.
|
||||
// This is returned by the API when a requested feature has not been
|
||||
// implemented.
|
||||
//
|
||||
// Deprecated: use errdefs.IsNotImplemented
|
||||
func IsErrNotImplemented(err error) bool {
|
||||
return errdefs.IsNotImplemented(err)
|
||||
}
|
||||
|
||||
// NewVersionError returns an error if the APIVersion required
|
||||
// if less than the current supported version
|
||||
func (cli *Client) NewVersionError(APIrequired, feature string) error {
|
||||
|
||||
+4
-9
@@ -23,14 +23,10 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
|
||||
if err != nil {
|
||||
return types.HijackedResponse{}, err
|
||||
}
|
||||
|
||||
apiPath := cli.getAPIPath(ctx, path, query)
|
||||
req, err := http.NewRequest(http.MethodPost, apiPath, bodyEncoded)
|
||||
req, err := cli.buildRequest(http.MethodPost, cli.getAPIPath(ctx, path, query), bodyEncoded, headers)
|
||||
if err != nil {
|
||||
return types.HijackedResponse{}, err
|
||||
}
|
||||
req = cli.addHeaders(req, headers)
|
||||
|
||||
conn, mediaType, err := cli.setupHijackConn(ctx, req, "tcp")
|
||||
if err != nil {
|
||||
return types.HijackedResponse{}, err
|
||||
@@ -64,7 +60,6 @@ func fallbackDial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) {
|
||||
}
|
||||
|
||||
func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto string) (net.Conn, string, error) {
|
||||
req.Host = cli.addr
|
||||
req.Header.Set("Connection", "Upgrade")
|
||||
req.Header.Set("Upgrade", proto)
|
||||
|
||||
@@ -80,8 +75,8 @@ func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto
|
||||
// state. Setting TCP KeepAlive on the socket connection will prohibit
|
||||
// ECONNTIMEOUT unless the socket connection truly is broken
|
||||
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
||||
tcpConn.SetKeepAlive(true)
|
||||
tcpConn.SetKeepAlivePeriod(30 * time.Second)
|
||||
_ = tcpConn.SetKeepAlive(true)
|
||||
_ = tcpConn.SetKeepAlivePeriod(30 * time.Second)
|
||||
}
|
||||
|
||||
clientconn := httputil.NewClientConn(conn, nil)
|
||||
@@ -96,7 +91,7 @@ func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto
|
||||
return nil, "", err
|
||||
}
|
||||
if resp.StatusCode != http.StatusSwitchingProtocols {
|
||||
resp.Body.Close()
|
||||
_ = resp.Body.Close()
|
||||
return nil, "", fmt.Errorf("unable to upgrade to %s, received %d", proto, resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
)
|
||||
|
||||
// ImageCreate creates a new image based on the parent options.
|
||||
@@ -32,6 +33,6 @@ func (cli *Client) ImageCreate(ctx context.Context, parentReference string, opti
|
||||
}
|
||||
|
||||
func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
return cli.post(ctx, "/images/create", query, nil, headers)
|
||||
}
|
||||
+2
-1
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/errdefs"
|
||||
)
|
||||
|
||||
@@ -49,6 +50,6 @@ func (cli *Client) ImagePush(ctx context.Context, image string, options types.Im
|
||||
}
|
||||
|
||||
func (cli *Client) tryImagePush(ctx context.Context, imageID string, query url.Values, registryAuth string) (serverResponse, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
return cli.post(ctx, "/images/"+imageID+"/push", query, nil, headers)
|
||||
}
|
||||
+1
-1
@@ -48,6 +48,6 @@ func (cli *Client) ImageSearch(ctx context.Context, term string, options types.I
|
||||
}
|
||||
|
||||
func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
return cli.get(ctx, "/images/search", query, headers)
|
||||
}
|
||||
+5
-5
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// CommonAPIClient is the common methods between stable and experimental versions of APIClient.
|
||||
@@ -47,8 +47,8 @@ type CommonAPIClient interface {
|
||||
type ContainerAPIClient interface {
|
||||
ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error)
|
||||
ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error)
|
||||
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.CreateResponse, error)
|
||||
ContainerDiff(ctx context.Context, container string) ([]container.ContainerChangeResponseItem, error)
|
||||
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error)
|
||||
ContainerDiff(ctx context.Context, container string) ([]container.FilesystemChange, error)
|
||||
ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error)
|
||||
ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error)
|
||||
ContainerExecInspect(ctx context.Context, execID string) (types.ContainerExecInspect, error)
|
||||
@@ -166,7 +166,7 @@ type SwarmAPIClient interface {
|
||||
type SystemAPIClient interface {
|
||||
Events(ctx context.Context, options types.EventsOptions) (<-chan events.Message, <-chan error)
|
||||
Info(ctx context.Context) (types.Info, error)
|
||||
RegistryLogin(ctx context.Context, auth types.AuthConfig) (registry.AuthenticateOKBody, error)
|
||||
RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error)
|
||||
DiskUsage(ctx context.Context, options types.DiskUsageOptions) (types.DiskUsage, error)
|
||||
Ping(ctx context.Context) (types.Ping, error)
|
||||
}
|
||||
@@ -176,7 +176,7 @@ type VolumeAPIClient interface {
|
||||
VolumeCreate(ctx context.Context, options volume.CreateOptions) (volume.Volume, error)
|
||||
VolumeInspect(ctx context.Context, volumeID string) (volume.Volume, error)
|
||||
VolumeInspectWithRaw(ctx context.Context, volumeID string) (volume.Volume, []byte, error)
|
||||
VolumeList(ctx context.Context, filter filters.Args) (volume.ListResponse, error)
|
||||
VolumeList(ctx context.Context, options volume.ListOptions) (volume.ListResponse, error)
|
||||
VolumeRemove(ctx context.Context, volumeID string, force bool) error
|
||||
VolumesPrune(ctx context.Context, pruneFilter filters.Args) (types.VolumesPruneReport, error)
|
||||
VolumeUpdate(ctx context.Context, volumeID string, version swarm.Version, options volume.UpdateOptions) error
|
||||
|
||||
+1
-2
@@ -5,13 +5,12 @@ import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
)
|
||||
|
||||
// RegistryLogin authenticates the docker server with a given docker registry.
|
||||
// It returns unauthorizedError when the authentication fails.
|
||||
func (cli *Client) RegistryLogin(ctx context.Context, auth types.AuthConfig) (registry.AuthenticateOKBody, error) {
|
||||
func (cli *Client) RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error) {
|
||||
resp, err := cli.post(ctx, "/auth", url.Values{}, auth, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
|
||||
|
||||
+3
-3
@@ -64,10 +64,10 @@ func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) {
|
||||
ping.BuilderVersion = types.BuilderVersion(bv)
|
||||
}
|
||||
if si := resp.header.Get("Swarm"); si != "" {
|
||||
parts := strings.SplitN(si, "/", 2)
|
||||
state, role, _ := strings.Cut(si, "/")
|
||||
ping.SwarmStatus = &swarm.Status{
|
||||
NodeState: swarm.LocalNodeState(parts[0]),
|
||||
ControlAvailable: len(parts) == 2 && parts[1] == "manager",
|
||||
NodeState: swarm.LocalNodeState(state),
|
||||
ControlAvailable: role == "manager",
|
||||
}
|
||||
}
|
||||
err := cli.checkResponseErr(resp)
|
||||
|
||||
+4
-3
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -67,12 +68,12 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
|
||||
}
|
||||
|
||||
func (cli *Client) tryPluginPrivileges(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
return cli.get(ctx, "/plugins/privileges", query, headers)
|
||||
}
|
||||
|
||||
func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, privileges types.PluginPrivileges, registryAuth string) (serverResponse, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
return cli.post(ctx, "/plugins/pull", query, privileges, headers)
|
||||
}
|
||||
|
||||
@@ -106,7 +107,7 @@ func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values,
|
||||
return nil, err
|
||||
}
|
||||
if !accept {
|
||||
return nil, pluginPermissionDenied{options.RemoteRef}
|
||||
return nil, errors.Errorf("permission denied while installing plugin %s", options.RemoteRef)
|
||||
}
|
||||
}
|
||||
return privileges, nil
|
||||
|
||||
+3
-1
@@ -3,11 +3,13 @@ package client // import "github.com/docker/docker/client"
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
)
|
||||
|
||||
// PluginPush pushes a plugin to a registry
|
||||
func (cli *Client) PluginPush(ctx context.Context, name string, registryAuth string) (io.ReadCloser, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
resp, err := cli.post(ctx, "/plugins/"+name+"/push", nil, nil, headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -34,6 +35,6 @@ func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types
|
||||
}
|
||||
|
||||
func (cli *Client) tryPluginUpgrade(ctx context.Context, query url.Values, privileges types.PluginPrivileges, name, registryAuth string) (serverResponse, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
headers := map[string][]string{registry.AuthHeader: {registryAuth}}
|
||||
return cli.post(ctx, "/plugins/"+name+"/upgrade", query, privileges, headers)
|
||||
}
|
||||
+4
-6
@@ -96,16 +96,14 @@ func (cli *Client) buildRequest(method, path string, body io.Reader, headers hea
|
||||
return nil, err
|
||||
}
|
||||
req = cli.addHeaders(req, headers)
|
||||
req.URL.Scheme = cli.scheme
|
||||
req.URL.Host = cli.addr
|
||||
|
||||
if cli.proto == "unix" || cli.proto == "npipe" {
|
||||
// For local communications, it doesn't matter what the host is. We just
|
||||
// need a valid and meaningful host name. (See #189)
|
||||
req.Host = "docker"
|
||||
// Override host header for non-tcp connections.
|
||||
req.Host = DummyHost
|
||||
}
|
||||
|
||||
req.URL.Host = cli.addr
|
||||
req.URL.Scheme = cli.scheme
|
||||
|
||||
if expectedPayload && req.Header.Get("Content-Type") == "" {
|
||||
req.Header.Set("Content-Type", "text/plain")
|
||||
}
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
@@ -21,7 +22,7 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec,
|
||||
}
|
||||
|
||||
if options.EncodedRegistryAuth != "" {
|
||||
headers["X-Registry-Auth"] = []string{options.EncodedRegistryAuth}
|
||||
headers[registry.AuthHeader] = []string{options.EncodedRegistryAuth}
|
||||
}
|
||||
|
||||
// Make sure containerSpec is not nil when no runtime is set or the runtime is set to container
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
)
|
||||
|
||||
@@ -23,7 +24,7 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
||||
}
|
||||
|
||||
if options.EncodedRegistryAuth != "" {
|
||||
headers["X-Registry-Auth"] = []string{options.EncodedRegistryAuth}
|
||||
headers[registry.AuthHeader] = []string{options.EncodedRegistryAuth}
|
||||
}
|
||||
|
||||
if options.RegistryAuthFrom != "" {
|
||||
|
||||
+3
-3
@@ -10,13 +10,13 @@ import (
|
||||
)
|
||||
|
||||
// VolumeList returns the volumes configured in the docker host.
|
||||
func (cli *Client) VolumeList(ctx context.Context, filter filters.Args) (volume.ListResponse, error) {
|
||||
func (cli *Client) VolumeList(ctx context.Context, options volume.ListOptions) (volume.ListResponse, error) {
|
||||
var volumes volume.ListResponse
|
||||
query := url.Values{}
|
||||
|
||||
if filter.Len() > 0 {
|
||||
if options.Filters.Len() > 0 {
|
||||
//nolint:staticcheck // ignore SA1019 for old code
|
||||
filterJSON, err := filters.ToParamWithVersion(cli.version, filter)
|
||||
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
|
||||
if err != nil {
|
||||
return volumes, err
|
||||
}
|
||||
|
||||
-133
@@ -1,133 +0,0 @@
|
||||
// Package ipamutils provides utility functions for ipam management
|
||||
package ipamutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// PredefinedLocalScopeDefaultNetworks contains a list of 31 IPv4 private networks with host size 16 and 12
|
||||
// (172.17-31.x.x/16, 192.168.x.x/20) which do not overlap with the networks in `PredefinedGlobalScopeDefaultNetworks`
|
||||
PredefinedLocalScopeDefaultNetworks []*net.IPNet
|
||||
// PredefinedGlobalScopeDefaultNetworks contains a list of 64K IPv4 private networks with host size 8
|
||||
// (10.x.x.x/24) which do not overlap with the networks in `PredefinedLocalScopeDefaultNetworks`
|
||||
PredefinedGlobalScopeDefaultNetworks []*net.IPNet
|
||||
mutex sync.Mutex
|
||||
localScopeDefaultNetworks = []*NetworkToSplit{{"172.17.0.0/16", 16}, {"172.18.0.0/16", 16}, {"172.19.0.0/16", 16},
|
||||
{"172.20.0.0/14", 16}, {"172.24.0.0/14", 16}, {"172.28.0.0/14", 16},
|
||||
{"192.168.0.0/16", 20}}
|
||||
globalScopeDefaultNetworks = []*NetworkToSplit{{"10.0.0.0/8", 24}}
|
||||
)
|
||||
|
||||
// NetworkToSplit represent a network that has to be split in chunks with mask length Size.
|
||||
// Each subnet in the set is derived from the Base pool. Base is to be passed
|
||||
// in CIDR format.
|
||||
// Example: a Base "10.10.0.0/16 with Size 24 will define the set of 256
|
||||
// 10.10.[0-255].0/24 address pools
|
||||
type NetworkToSplit struct {
|
||||
Base string `json:"base"`
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
if PredefinedGlobalScopeDefaultNetworks, err = splitNetworks(globalScopeDefaultNetworks); err != nil {
|
||||
panic("failed to initialize the global scope default address pool: " + err.Error())
|
||||
}
|
||||
|
||||
if PredefinedLocalScopeDefaultNetworks, err = splitNetworks(localScopeDefaultNetworks); err != nil {
|
||||
panic("failed to initialize the local scope default address pool: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// configDefaultNetworks configures local as well global default pool based on input
|
||||
func configDefaultNetworks(defaultAddressPool []*NetworkToSplit, result *[]*net.IPNet) error {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
defaultNetworks, err := splitNetworks(defaultAddressPool)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*result = defaultNetworks
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetGlobalScopeDefaultNetworks returns PredefinedGlobalScopeDefaultNetworks
|
||||
func GetGlobalScopeDefaultNetworks() []*net.IPNet {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
return PredefinedGlobalScopeDefaultNetworks
|
||||
}
|
||||
|
||||
// GetLocalScopeDefaultNetworks returns PredefinedLocalScopeDefaultNetworks
|
||||
func GetLocalScopeDefaultNetworks() []*net.IPNet {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
return PredefinedLocalScopeDefaultNetworks
|
||||
}
|
||||
|
||||
// ConfigGlobalScopeDefaultNetworks configures global default pool.
|
||||
// Ideally this will be called from SwarmKit as part of swarm init
|
||||
func ConfigGlobalScopeDefaultNetworks(defaultAddressPool []*NetworkToSplit) error {
|
||||
if defaultAddressPool == nil {
|
||||
defaultAddressPool = globalScopeDefaultNetworks
|
||||
}
|
||||
return configDefaultNetworks(defaultAddressPool, &PredefinedGlobalScopeDefaultNetworks)
|
||||
}
|
||||
|
||||
// ConfigLocalScopeDefaultNetworks configures local default pool.
|
||||
// Ideally this will be called during libnetwork init
|
||||
func ConfigLocalScopeDefaultNetworks(defaultAddressPool []*NetworkToSplit) error {
|
||||
if defaultAddressPool == nil {
|
||||
return nil
|
||||
}
|
||||
return configDefaultNetworks(defaultAddressPool, &PredefinedLocalScopeDefaultNetworks)
|
||||
}
|
||||
|
||||
// splitNetworks takes a slice of networks, split them accordingly and returns them
|
||||
func splitNetworks(list []*NetworkToSplit) ([]*net.IPNet, error) {
|
||||
localPools := make([]*net.IPNet, 0, len(list))
|
||||
|
||||
for _, p := range list {
|
||||
_, b, err := net.ParseCIDR(p.Base)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid base pool %q: %v", p.Base, err)
|
||||
}
|
||||
ones, _ := b.Mask.Size()
|
||||
if p.Size <= 0 || p.Size < ones {
|
||||
return nil, fmt.Errorf("invalid pools size: %d", p.Size)
|
||||
}
|
||||
localPools = append(localPools, splitNetwork(p.Size, b)...)
|
||||
}
|
||||
return localPools, nil
|
||||
}
|
||||
|
||||
func splitNetwork(size int, base *net.IPNet) []*net.IPNet {
|
||||
one, bits := base.Mask.Size()
|
||||
mask := net.CIDRMask(size, bits)
|
||||
n := 1 << uint(size-one)
|
||||
s := uint(bits - size)
|
||||
list := make([]*net.IPNet, 0, n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
ip := copyIP(base.IP)
|
||||
addIntToIP(ip, uint(i<<s))
|
||||
list = append(list, &net.IPNet{IP: ip, Mask: mask})
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func copyIP(from net.IP) net.IP {
|
||||
ip := make([]byte, len(from))
|
||||
copy(ip, from)
|
||||
return ip
|
||||
}
|
||||
|
||||
func addIntToIP(array net.IP, ordinal uint) {
|
||||
for i := len(array) - 1; i >= 0; i-- {
|
||||
array[i] |= (byte)(ordinal & 0xff)
|
||||
ordinal >>= 8
|
||||
}
|
||||
}
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
package opts
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
types "github.com/docker/docker/libnetwork/ipamutils"
|
||||
)
|
||||
|
||||
// PoolsOpt is a Value type for parsing the default address pools definitions
|
||||
type PoolsOpt struct {
|
||||
Values []*types.NetworkToSplit
|
||||
}
|
||||
|
||||
// UnmarshalJSON fills values structure info from JSON input
|
||||
func (p *PoolsOpt) UnmarshalJSON(raw []byte) error {
|
||||
return json.Unmarshal(raw, &(p.Values))
|
||||
}
|
||||
|
||||
// Set predefined pools
|
||||
func (p *PoolsOpt) Set(value string) error {
|
||||
csvReader := csv.NewReader(strings.NewReader(value))
|
||||
fields, err := csvReader.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
poolsDef := types.NetworkToSplit{}
|
||||
|
||||
for _, field := range fields {
|
||||
parts := strings.SplitN(field, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
|
||||
}
|
||||
|
||||
key := strings.ToLower(parts[0])
|
||||
value := strings.ToLower(parts[1])
|
||||
|
||||
switch key {
|
||||
case "base":
|
||||
poolsDef.Base = value
|
||||
case "size":
|
||||
size, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid size value: %q (must be integer): %v", value, err)
|
||||
}
|
||||
poolsDef.Size = size
|
||||
default:
|
||||
return fmt.Errorf("unexpected key '%s' in '%s'", key, field)
|
||||
}
|
||||
}
|
||||
|
||||
p.Values = append(p.Values, &poolsDef)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns the type of this option
|
||||
func (p *PoolsOpt) Type() string {
|
||||
return "pool-options"
|
||||
}
|
||||
|
||||
// String returns a string repr of this option
|
||||
func (p *PoolsOpt) String() string {
|
||||
var pools []string
|
||||
for _, pool := range p.Values {
|
||||
repr := fmt.Sprintf("%s %d", pool.Base, pool.Size)
|
||||
pools = append(pools, repr)
|
||||
}
|
||||
return strings.Join(pools, ", ")
|
||||
}
|
||||
|
||||
// Value returns the mounts
|
||||
func (p *PoolsOpt) Value() []*types.NetworkToSplit {
|
||||
return p.Values
|
||||
}
|
||||
|
||||
// Name returns the flag name of this option
|
||||
func (p *PoolsOpt) Name() string {
|
||||
return "default-address-pools"
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ValidateEnv validates an environment variable and returns it.
|
||||
// If no value is specified, it obtains its value from the current environment
|
||||
//
|
||||
// As on ParseEnvFile and related to #16585, environment variable names
|
||||
// are not validate whatsoever, it's up to application inside docker
|
||||
// to validate them or not.
|
||||
//
|
||||
// The only validation here is to check if name is empty, per #25099
|
||||
func ValidateEnv(val string) (string, error) {
|
||||
arr := strings.SplitN(val, "=", 2)
|
||||
if arr[0] == "" {
|
||||
return "", errors.New("invalid environment variable: " + val)
|
||||
}
|
||||
if len(arr) > 1 {
|
||||
return val, nil
|
||||
}
|
||||
if envVal, ok := os.LookupEnv(arr[0]); ok {
|
||||
return arr[0] + "=" + envVal, nil
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
-186
@@ -1,186 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultHTTPPort Default HTTP Port used if only the protocol is provided to -H flag e.g. dockerd -H tcp://
|
||||
// These are the IANA registered port numbers for use with Docker
|
||||
// see http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=docker
|
||||
DefaultHTTPPort = 2375 // Default HTTP Port
|
||||
// DefaultTLSHTTPPort Default HTTP Port used when TLS enabled
|
||||
DefaultTLSHTTPPort = 2376 // Default TLS encrypted HTTP Port
|
||||
// DefaultUnixSocket Path for the unix socket.
|
||||
// Docker daemon by default always listens on the default unix socket
|
||||
DefaultUnixSocket = "/var/run/docker.sock"
|
||||
// DefaultTCPHost constant defines the default host string used by docker on Windows
|
||||
DefaultTCPHost = "tcp://" + DefaultHTTPHost + ":2375"
|
||||
// DefaultTLSHost constant defines the default host string used by docker for TLS sockets
|
||||
DefaultTLSHost = "tcp://" + DefaultHTTPHost + ":2376"
|
||||
// DefaultNamedPipe defines the default named pipe used by docker on Windows
|
||||
DefaultNamedPipe = `//./pipe/docker_engine`
|
||||
// HostGatewayName is the string value that can be passed
|
||||
// to the IPAddr section in --add-host that is replaced by
|
||||
// the value of HostGatewayIP daemon config value
|
||||
HostGatewayName = "host-gateway"
|
||||
)
|
||||
|
||||
// ValidateHost validates that the specified string is a valid host and returns it.
|
||||
func ValidateHost(val string) (string, error) {
|
||||
host := strings.TrimSpace(val)
|
||||
// The empty string means default and is not handled by parseDaemonHost
|
||||
if host != "" {
|
||||
_, err := parseDaemonHost(host)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
}
|
||||
// Note: unlike most flag validators, we don't return the mutated value here
|
||||
// we need to know what the user entered later (using ParseHost) to adjust for TLS
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// ParseHost and set defaults for a Daemon host string.
|
||||
// defaultToTLS is preferred over defaultToUnixXDG.
|
||||
func ParseHost(defaultToTLS, defaultToUnixXDG bool, val string) (string, error) {
|
||||
host := strings.TrimSpace(val)
|
||||
if host == "" {
|
||||
if defaultToTLS {
|
||||
host = DefaultTLSHost
|
||||
} else if defaultToUnixXDG {
|
||||
runtimeDir, err := homedir.GetRuntimeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
socket := filepath.Join(runtimeDir, "docker.sock")
|
||||
host = "unix://" + socket
|
||||
} else {
|
||||
host = DefaultHost
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
host, err = parseDaemonHost(host)
|
||||
if err != nil {
|
||||
return val, err
|
||||
}
|
||||
}
|
||||
return host, nil
|
||||
}
|
||||
|
||||
// parseDaemonHost parses the specified address and returns an address that will be used as the host.
|
||||
// Depending on the address specified, this may return one of the global Default* strings defined in hosts.go.
|
||||
func parseDaemonHost(addr string) (string, error) {
|
||||
addrParts := strings.SplitN(addr, "://", 2)
|
||||
if len(addrParts) == 1 && addrParts[0] != "" {
|
||||
addrParts = []string{"tcp", addrParts[0]}
|
||||
}
|
||||
|
||||
switch addrParts[0] {
|
||||
case "tcp":
|
||||
return ParseTCPAddr(addr, DefaultTCPHost)
|
||||
case "unix":
|
||||
return parseSimpleProtoAddr("unix", addrParts[1], DefaultUnixSocket)
|
||||
case "npipe":
|
||||
return parseSimpleProtoAddr("npipe", addrParts[1], DefaultNamedPipe)
|
||||
case "fd":
|
||||
return addr, nil
|
||||
default:
|
||||
return "", errors.Errorf("invalid bind address (%s): unsupported proto '%s'", addr, addrParts[0])
|
||||
}
|
||||
}
|
||||
|
||||
// parseSimpleProtoAddr parses and validates that the specified address is a valid
|
||||
// socket address for simple protocols like unix and npipe. It returns a formatted
|
||||
// socket address, either using the address parsed from addr, or the contents of
|
||||
// defaultAddr if addr is a blank string.
|
||||
func parseSimpleProtoAddr(proto, addr, defaultAddr string) (string, error) {
|
||||
addr = strings.TrimPrefix(addr, proto+"://")
|
||||
if strings.Contains(addr, "://") {
|
||||
return "", errors.Errorf("invalid proto, expected %s: %s", proto, addr)
|
||||
}
|
||||
if addr == "" {
|
||||
addr = defaultAddr
|
||||
}
|
||||
return proto + "://" + addr, nil
|
||||
}
|
||||
|
||||
// ParseTCPAddr parses and validates that the specified address is a valid TCP
|
||||
// address. It returns a formatted TCP address, either using the address parsed
|
||||
// from tryAddr, or the contents of defaultAddr if tryAddr is a blank string.
|
||||
// tryAddr is expected to have already been Trim()'d
|
||||
// defaultAddr must be in the full `tcp://host:port` form
|
||||
func ParseTCPAddr(tryAddr string, defaultAddr string) (string, error) {
|
||||
def, err := parseTCPAddr(defaultAddr, true)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "invalid default address (%s)", defaultAddr)
|
||||
}
|
||||
|
||||
addr, err := parseTCPAddr(tryAddr, false)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "invalid bind address (%s)", tryAddr)
|
||||
}
|
||||
|
||||
host := addr.Hostname()
|
||||
if host == "" {
|
||||
host = def.Hostname()
|
||||
}
|
||||
port := addr.Port()
|
||||
if port == "" {
|
||||
port = def.Port()
|
||||
}
|
||||
|
||||
return "tcp://" + net.JoinHostPort(host, port), nil
|
||||
}
|
||||
|
||||
// parseTCPAddr parses the given addr and validates if it is in the expected
|
||||
// format. If strict is enabled, the address must contain a scheme (tcp://),
|
||||
// a host (or IP-address) and a port number.
|
||||
func parseTCPAddr(address string, strict bool) (*url.URL, error) {
|
||||
if !strict && !strings.Contains(address, "://") {
|
||||
address = "tcp://" + address
|
||||
}
|
||||
parsedURL, err := url.Parse(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if parsedURL.Scheme != "tcp" {
|
||||
return nil, errors.Errorf("unsupported proto '%s'", parsedURL.Scheme)
|
||||
}
|
||||
if parsedURL.Path != "" {
|
||||
return nil, errors.New("should not contain a path element")
|
||||
}
|
||||
if strict && parsedURL.Host == "" {
|
||||
return nil, errors.New("no host or IP address")
|
||||
}
|
||||
if parsedURL.Port() != "" || strict {
|
||||
if p, err := strconv.Atoi(parsedURL.Port()); err != nil || p == 0 {
|
||||
return nil, errors.Errorf("invalid port: %s", parsedURL.Port())
|
||||
}
|
||||
}
|
||||
return parsedURL, nil
|
||||
}
|
||||
|
||||
// ValidateExtraHost validates that the specified string is a valid extrahost and returns it.
|
||||
// ExtraHost is in the form of name:ip where the ip has to be a valid ip (IPv4 or IPv6).
|
||||
func ValidateExtraHost(val string) (string, error) {
|
||||
// allow for IPv6 addresses in extra hosts by only splitting on first ":"
|
||||
arr := strings.SplitN(val, ":", 2)
|
||||
if len(arr) != 2 || len(arr[0]) == 0 {
|
||||
return "", errors.Errorf("bad format for add-host: %q", val)
|
||||
}
|
||||
// Skip IPaddr validation for special "host-gateway" string
|
||||
if arr[1] != HostGatewayName {
|
||||
if _, err := ValidateIPAddress(arr[1]); err != nil {
|
||||
return "", errors.Errorf("invalid IP address in add-host: %q", arr[1])
|
||||
}
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
const (
|
||||
// DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. dockerd -H tcp://:8080
|
||||
DefaultHTTPHost = "localhost"
|
||||
|
||||
// DefaultHost constant defines the default host string used by docker on other hosts than Windows
|
||||
DefaultHost = "unix://" + DefaultUnixSocket
|
||||
)
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
const (
|
||||
// TODO Windows. Identify bug in GOLang 1.5.1+ and/or Windows Server 2016 TP5.
|
||||
//
|
||||
// On Windows, this mitigates a problem with the default options of running
|
||||
// a docker client against a local docker daemon on TP5.
|
||||
//
|
||||
// What was found that if the default host is "localhost", even if the client
|
||||
// (and daemon as this is local) is not physically on a network, and the DNS
|
||||
// cache is flushed (ipconfig /flushdns), then the client will pause for
|
||||
// exactly one second when connecting to the daemon for calls. For example
|
||||
// using docker run windowsservercore cmd, the CLI will send a create followed
|
||||
// by an attach. You see the delay between the attach finishing and the attach
|
||||
// being seen by the daemon.
|
||||
//
|
||||
// Here's some daemon debug logs with additional debug spew put in. The
|
||||
// AfterWriteJSON log is the very last thing the daemon does as part of the
|
||||
// create call. The POST /attach is the second CLI call. Notice the second
|
||||
// time gap.
|
||||
//
|
||||
// time="2015-11-06T13:38:37.259627400-08:00" level=debug msg="After createRootfs"
|
||||
// time="2015-11-06T13:38:37.263626300-08:00" level=debug msg="After setHostConfig"
|
||||
// time="2015-11-06T13:38:37.267631200-08:00" level=debug msg="before createContainerPl...."
|
||||
// time="2015-11-06T13:38:37.271629500-08:00" level=debug msg=ToDiskLocking....
|
||||
// time="2015-11-06T13:38:37.275643200-08:00" level=debug msg="loggin event...."
|
||||
// time="2015-11-06T13:38:37.277627600-08:00" level=debug msg="logged event...."
|
||||
// time="2015-11-06T13:38:37.279631800-08:00" level=debug msg="In defer func"
|
||||
// time="2015-11-06T13:38:37.282628100-08:00" level=debug msg="After daemon.create"
|
||||
// time="2015-11-06T13:38:37.286651700-08:00" level=debug msg="return 2"
|
||||
// time="2015-11-06T13:38:37.289629500-08:00" level=debug msg="Returned from daemon.ContainerCreate"
|
||||
// time="2015-11-06T13:38:37.311629100-08:00" level=debug msg="After WriteJSON"
|
||||
// ... 1 second gap here....
|
||||
// time="2015-11-06T13:38:38.317866200-08:00" level=debug msg="Calling POST /v1.22/containers/984758282b842f779e805664b2c95d563adc9a979c8a3973e68c807843ee4757/attach"
|
||||
// time="2015-11-06T13:38:38.326882500-08:00" level=info msg="POST /v1.22/containers/984758282b842f779e805664b2c95d563adc9a979c8a3973e68c807843ee4757/attach?stderr=1&stdin=1&stdout=1&stream=1"
|
||||
//
|
||||
// We suspect this is either a bug introduced in GOLang 1.5.1, or that a change
|
||||
// in GOLang 1.5.1 (from 1.4.3) is exposing a bug in Windows. In theory,
|
||||
// the Windows networking stack is supposed to resolve "localhost" internally,
|
||||
// without hitting DNS, or even reading the hosts file (which is why localhost
|
||||
// is commented out in the hosts file on Windows).
|
||||
//
|
||||
// We have validated that working around this using the actual IPv4 localhost
|
||||
// address does not cause the delay.
|
||||
//
|
||||
// This does not occur with the docker client built with 1.4.3 on the same
|
||||
// Windows build, regardless of whether the daemon is built using 1.5.1
|
||||
// or 1.4.3. It does not occur on Linux. We also verified we see the same thing
|
||||
// on a cross-compiled Windows binary (from Linux).
|
||||
//
|
||||
// Final note: This is a mitigation, not a 'real' fix. It is still susceptible
|
||||
// to the delay if a user were to do 'docker run -H=tcp://localhost:2375...'
|
||||
// explicitly.
|
||||
|
||||
// DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. dockerd -H tcp://:8080
|
||||
DefaultHTTPHost = "127.0.0.1"
|
||||
|
||||
// DefaultHost constant defines the default host string used by docker on Windows
|
||||
DefaultHost = "npipe://" + DefaultNamedPipe
|
||||
)
|
||||
-348
@@ -1,348 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
var (
|
||||
alphaRegexp = regexp.MustCompile(`[a-zA-Z]`)
|
||||
domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`)
|
||||
)
|
||||
|
||||
// ListOpts holds a list of values and a validation function.
|
||||
type ListOpts struct {
|
||||
values *[]string
|
||||
validator ValidatorFctType
|
||||
}
|
||||
|
||||
// NewListOpts creates a new ListOpts with the specified validator.
|
||||
func NewListOpts(validator ValidatorFctType) ListOpts {
|
||||
var values []string
|
||||
return *NewListOptsRef(&values, validator)
|
||||
}
|
||||
|
||||
// NewListOptsRef creates a new ListOpts with the specified values and validator.
|
||||
func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
|
||||
return &ListOpts{
|
||||
values: values,
|
||||
validator: validator,
|
||||
}
|
||||
}
|
||||
|
||||
func (opts *ListOpts) String() string {
|
||||
if len(*opts.values) == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%v", *opts.values)
|
||||
}
|
||||
|
||||
// Set validates if needed the input value and adds it to the
|
||||
// internal slice.
|
||||
func (opts *ListOpts) Set(value string) error {
|
||||
if opts.validator != nil {
|
||||
v, err := opts.validator(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value = v
|
||||
}
|
||||
*opts.values = append(*opts.values, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete removes the specified element from the slice.
|
||||
func (opts *ListOpts) Delete(key string) {
|
||||
for i, k := range *opts.values {
|
||||
if k == key {
|
||||
*opts.values = append((*opts.values)[:i], (*opts.values)[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetMap returns the content of values in a map in order to avoid
|
||||
// duplicates.
|
||||
func (opts *ListOpts) GetMap() map[string]struct{} {
|
||||
ret := make(map[string]struct{})
|
||||
for _, k := range *opts.values {
|
||||
ret[k] = struct{}{}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetAll returns the values of slice.
|
||||
func (opts *ListOpts) GetAll() []string {
|
||||
return *opts.values
|
||||
}
|
||||
|
||||
// GetAllOrEmpty returns the values of the slice
|
||||
// or an empty slice when there are no values.
|
||||
func (opts *ListOpts) GetAllOrEmpty() []string {
|
||||
v := *opts.values
|
||||
if v == nil {
|
||||
return make([]string, 0)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Get checks the existence of the specified key.
|
||||
func (opts *ListOpts) Get(key string) bool {
|
||||
for _, k := range *opts.values {
|
||||
if k == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Len returns the amount of element in the slice.
|
||||
func (opts *ListOpts) Len() int {
|
||||
return len(*opts.values)
|
||||
}
|
||||
|
||||
// Type returns a string name for this Option type
|
||||
func (opts *ListOpts) Type() string {
|
||||
return "list"
|
||||
}
|
||||
|
||||
// WithValidator returns the ListOpts with validator set.
|
||||
func (opts *ListOpts) WithValidator(validator ValidatorFctType) *ListOpts {
|
||||
opts.validator = validator
|
||||
return opts
|
||||
}
|
||||
|
||||
// NamedOption is an interface that list and map options
|
||||
// with names implement.
|
||||
type NamedOption interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
// NamedListOpts is a ListOpts with a configuration name.
|
||||
// This struct is useful to keep reference to the assigned
|
||||
// field name in the internal configuration struct.
|
||||
type NamedListOpts struct {
|
||||
name string
|
||||
ListOpts
|
||||
}
|
||||
|
||||
var _ NamedOption = &NamedListOpts{}
|
||||
|
||||
// NewNamedListOptsRef creates a reference to a new NamedListOpts struct.
|
||||
func NewNamedListOptsRef(name string, values *[]string, validator ValidatorFctType) *NamedListOpts {
|
||||
return &NamedListOpts{
|
||||
name: name,
|
||||
ListOpts: *NewListOptsRef(values, validator),
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the name of the NamedListOpts in the configuration.
|
||||
func (o *NamedListOpts) Name() string {
|
||||
return o.name
|
||||
}
|
||||
|
||||
// MapOpts holds a map of values and a validation function.
|
||||
type MapOpts struct {
|
||||
values map[string]string
|
||||
validator ValidatorFctType
|
||||
}
|
||||
|
||||
// Set validates if needed the input value and add it to the
|
||||
// internal map, by splitting on '='.
|
||||
func (opts *MapOpts) Set(value string) error {
|
||||
if opts.validator != nil {
|
||||
v, err := opts.validator(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value = v
|
||||
}
|
||||
vals := strings.SplitN(value, "=", 2)
|
||||
if len(vals) == 1 {
|
||||
(opts.values)[vals[0]] = ""
|
||||
} else {
|
||||
(opts.values)[vals[0]] = vals[1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAll returns the values of MapOpts as a map.
|
||||
func (opts *MapOpts) GetAll() map[string]string {
|
||||
return opts.values
|
||||
}
|
||||
|
||||
func (opts *MapOpts) String() string {
|
||||
return fmt.Sprintf("%v", opts.values)
|
||||
}
|
||||
|
||||
// Type returns a string name for this Option type
|
||||
func (opts *MapOpts) Type() string {
|
||||
return "map"
|
||||
}
|
||||
|
||||
// NewMapOpts creates a new MapOpts with the specified map of values and a validator.
|
||||
func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
|
||||
if values == nil {
|
||||
values = make(map[string]string)
|
||||
}
|
||||
return &MapOpts{
|
||||
values: values,
|
||||
validator: validator,
|
||||
}
|
||||
}
|
||||
|
||||
// NamedMapOpts is a MapOpts struct with a configuration name.
|
||||
// This struct is useful to keep reference to the assigned
|
||||
// field name in the internal configuration struct.
|
||||
type NamedMapOpts struct {
|
||||
name string
|
||||
MapOpts
|
||||
}
|
||||
|
||||
var _ NamedOption = &NamedMapOpts{}
|
||||
|
||||
// NewNamedMapOpts creates a reference to a new NamedMapOpts struct.
|
||||
func NewNamedMapOpts(name string, values map[string]string, validator ValidatorFctType) *NamedMapOpts {
|
||||
return &NamedMapOpts{
|
||||
name: name,
|
||||
MapOpts: *NewMapOpts(values, validator),
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the name of the NamedMapOpts in the configuration.
|
||||
func (o *NamedMapOpts) Name() string {
|
||||
return o.name
|
||||
}
|
||||
|
||||
// ValidatorFctType defines a validator function that returns a validated string and/or an error.
|
||||
type ValidatorFctType func(val string) (string, error)
|
||||
|
||||
// ValidatorFctListType defines a validator function that returns a validated list of string and/or an error
|
||||
type ValidatorFctListType func(val string) ([]string, error)
|
||||
|
||||
// ValidateIPAddress validates an Ip address.
|
||||
func ValidateIPAddress(val string) (string, error) {
|
||||
var ip = net.ParseIP(strings.TrimSpace(val))
|
||||
if ip != nil {
|
||||
return ip.String(), nil
|
||||
}
|
||||
return "", fmt.Errorf("%s is not an ip address", val)
|
||||
}
|
||||
|
||||
// ValidateDNSSearch validates domain for resolvconf search configuration.
|
||||
// A zero length domain is represented by a dot (.).
|
||||
func ValidateDNSSearch(val string) (string, error) {
|
||||
if val = strings.Trim(val, " "); val == "." {
|
||||
return val, nil
|
||||
}
|
||||
return validateDomain(val)
|
||||
}
|
||||
|
||||
func validateDomain(val string) (string, error) {
|
||||
if alphaRegexp.FindString(val) == "" {
|
||||
return "", fmt.Errorf("%s is not a valid domain", val)
|
||||
}
|
||||
ns := domainRegexp.FindSubmatch([]byte(val))
|
||||
if len(ns) > 0 && len(ns[1]) < 255 {
|
||||
return string(ns[1]), nil
|
||||
}
|
||||
return "", fmt.Errorf("%s is not a valid domain", val)
|
||||
}
|
||||
|
||||
// ValidateLabel validates that the specified string is a valid label,
|
||||
// it does not use the reserved namespaces com.docker.*, io.docker.*, org.dockerproject.*
|
||||
// and returns it.
|
||||
// Labels are in the form on key=value.
|
||||
func ValidateLabel(val string) (string, error) {
|
||||
if strings.Count(val, "=") < 1 {
|
||||
return "", fmt.Errorf("bad attribute format: %s", val)
|
||||
}
|
||||
|
||||
lowered := strings.ToLower(val)
|
||||
if strings.HasPrefix(lowered, "com.docker.") || strings.HasPrefix(lowered, "io.docker.") ||
|
||||
strings.HasPrefix(lowered, "org.dockerproject.") {
|
||||
return "", fmt.Errorf(
|
||||
"label %s is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use",
|
||||
val)
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// ValidateSingleGenericResource validates that a single entry in the
|
||||
// generic resource list is valid.
|
||||
// i.e 'GPU=UID1' is valid however 'GPU:UID1' or 'UID1' isn't
|
||||
func ValidateSingleGenericResource(val string) (string, error) {
|
||||
if strings.Count(val, "=") < 1 {
|
||||
return "", fmt.Errorf("invalid node-generic-resource format `%s` expected `name=value`", val)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// ParseLink parses and validates the specified string as a link format (name:alias)
|
||||
func ParseLink(val string) (string, string, error) {
|
||||
if val == "" {
|
||||
return "", "", fmt.Errorf("empty string specified for links")
|
||||
}
|
||||
arr := strings.Split(val, ":")
|
||||
if len(arr) > 2 {
|
||||
return "", "", fmt.Errorf("bad format for links: %s", val)
|
||||
}
|
||||
if len(arr) == 1 {
|
||||
return val, val, nil
|
||||
}
|
||||
// This is kept because we can actually get a HostConfig with links
|
||||
// from an already created container and the format is not `foo:bar`
|
||||
// but `/foo:/c1/bar`
|
||||
if strings.HasPrefix(arr[0], "/") {
|
||||
_, alias := path.Split(arr[1])
|
||||
return arr[0][1:], alias, nil
|
||||
}
|
||||
return arr[0], arr[1], nil
|
||||
}
|
||||
|
||||
// MemBytes is a type for human readable memory bytes (like 128M, 2g, etc)
|
||||
type MemBytes int64
|
||||
|
||||
// String returns the string format of the human readable memory bytes
|
||||
func (m *MemBytes) String() string {
|
||||
// NOTE: In spf13/pflag/flag.go, "0" is considered as "zero value" while "0 B" is not.
|
||||
// We return "0" in case value is 0 here so that the default value is hidden.
|
||||
// (Sometimes "default 0 B" is actually misleading)
|
||||
if m.Value() != 0 {
|
||||
return units.BytesSize(float64(m.Value()))
|
||||
}
|
||||
return "0"
|
||||
}
|
||||
|
||||
// Set sets the value of the MemBytes by passing a string
|
||||
func (m *MemBytes) Set(value string) error {
|
||||
val, err := units.RAMInBytes(value)
|
||||
*m = MemBytes(val)
|
||||
return err
|
||||
}
|
||||
|
||||
// Type returns the type
|
||||
func (m *MemBytes) Type() string {
|
||||
return "bytes"
|
||||
}
|
||||
|
||||
// Value returns the value in int64
|
||||
func (m *MemBytes) Value() int64 {
|
||||
return int64(*m)
|
||||
}
|
||||
|
||||
// UnmarshalJSON is the customized unmarshaler for MemBytes
|
||||
func (m *MemBytes) UnmarshalJSON(s []byte) error {
|
||||
if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' {
|
||||
return fmt.Errorf("invalid size: %q", s)
|
||||
}
|
||||
val, err := units.RAMInBytes(string(s[1 : len(s)-1]))
|
||||
*m = MemBytes(val)
|
||||
return err
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
// RuntimeOpt defines a map of Runtimes
|
||||
type RuntimeOpt struct {
|
||||
name string
|
||||
stockRuntimeName string
|
||||
values *map[string]types.Runtime
|
||||
}
|
||||
|
||||
// NewNamedRuntimeOpt creates a new RuntimeOpt
|
||||
func NewNamedRuntimeOpt(name string, ref *map[string]types.Runtime, stockRuntime string) *RuntimeOpt {
|
||||
if ref == nil {
|
||||
ref = &map[string]types.Runtime{}
|
||||
}
|
||||
return &RuntimeOpt{name: name, values: ref, stockRuntimeName: stockRuntime}
|
||||
}
|
||||
|
||||
// Name returns the name of the NamedListOpts in the configuration.
|
||||
func (o *RuntimeOpt) Name() string {
|
||||
return o.name
|
||||
}
|
||||
|
||||
// Set validates and updates the list of Runtimes
|
||||
func (o *RuntimeOpt) Set(val string) error {
|
||||
parts := strings.SplitN(val, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid runtime argument: %s", val)
|
||||
}
|
||||
|
||||
parts[0] = strings.TrimSpace(parts[0])
|
||||
parts[1] = strings.TrimSpace(parts[1])
|
||||
if parts[0] == "" || parts[1] == "" {
|
||||
return fmt.Errorf("invalid runtime argument: %s", val)
|
||||
}
|
||||
|
||||
parts[0] = strings.ToLower(parts[0])
|
||||
if parts[0] == o.stockRuntimeName {
|
||||
return fmt.Errorf("runtime name '%s' is reserved", o.stockRuntimeName)
|
||||
}
|
||||
|
||||
if _, ok := (*o.values)[parts[0]]; ok {
|
||||
return fmt.Errorf("runtime '%s' was already defined", parts[0])
|
||||
}
|
||||
|
||||
(*o.values)[parts[0]] = types.Runtime{Path: parts[1]}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns Runtime values as a string.
|
||||
func (o *RuntimeOpt) String() string {
|
||||
var out []string
|
||||
for k := range *o.values {
|
||||
out = append(out, k)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", out)
|
||||
}
|
||||
|
||||
// GetMap returns a map of Runtimes (name: path)
|
||||
func (o *RuntimeOpt) GetMap() map[string]types.Runtime {
|
||||
if o.values != nil {
|
||||
return *o.values
|
||||
}
|
||||
|
||||
return map[string]types.Runtime{}
|
||||
}
|
||||
|
||||
// Type returns the type of the option
|
||||
func (o *RuntimeOpt) Type() string {
|
||||
return "runtime"
|
||||
}
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
package opts // import "github.com/docker/docker/opts"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
// UlimitOpt defines a map of Ulimits
|
||||
type UlimitOpt struct {
|
||||
values *map[string]*units.Ulimit
|
||||
}
|
||||
|
||||
// NewUlimitOpt creates a new UlimitOpt
|
||||
func NewUlimitOpt(ref *map[string]*units.Ulimit) *UlimitOpt {
|
||||
if ref == nil {
|
||||
ref = &map[string]*units.Ulimit{}
|
||||
}
|
||||
return &UlimitOpt{ref}
|
||||
}
|
||||
|
||||
// Set validates a Ulimit and sets its name as a key in UlimitOpt
|
||||
func (o *UlimitOpt) Set(val string) error {
|
||||
l, err := units.ParseUlimit(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
(*o.values)[l.Name] = l
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns Ulimit values as a string.
|
||||
func (o *UlimitOpt) String() string {
|
||||
var out []string
|
||||
for _, v := range *o.values {
|
||||
out = append(out, v.String())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", out)
|
||||
}
|
||||
|
||||
// GetList returns a slice of pointers to Ulimits.
|
||||
func (o *UlimitOpt) GetList() []*units.Ulimit {
|
||||
var ulimits []*units.Ulimit
|
||||
for _, v := range *o.values {
|
||||
ulimits = append(ulimits, v)
|
||||
}
|
||||
|
||||
return ulimits
|
||||
}
|
||||
|
||||
// Type returns the option type
|
||||
func (o *UlimitOpt) Type() string {
|
||||
return "ulimit"
|
||||
}
|
||||
|
||||
// NamedUlimitOpt defines a named map of Ulimits
|
||||
type NamedUlimitOpt struct {
|
||||
name string
|
||||
UlimitOpt
|
||||
}
|
||||
|
||||
var _ NamedOption = &NamedUlimitOpt{}
|
||||
|
||||
// NewNamedUlimitOpt creates a new NamedUlimitOpt
|
||||
func NewNamedUlimitOpt(name string, ref *map[string]*units.Ulimit) *NamedUlimitOpt {
|
||||
if ref == nil {
|
||||
ref = &map[string]*units.Ulimit{}
|
||||
}
|
||||
return &NamedUlimitOpt{
|
||||
name: name,
|
||||
UlimitOpt: *NewUlimitOpt(ref),
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the option name
|
||||
func (o *NamedUlimitOpt) Name() string {
|
||||
return o.name
|
||||
}
|
||||
-1
@@ -1 +0,0 @@
|
||||
This code provides helper functions for dealing with archive files.
|
||||
+222
-214
@@ -1,3 +1,4 @@
|
||||
// Package archive provides helper functions for dealing with archive files.
|
||||
package archive // import "github.com/docker/docker/pkg/archive"
|
||||
|
||||
import (
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -28,7 +30,6 @@ import (
|
||||
"github.com/moby/sys/sequential"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
exec "golang.org/x/sys/execabs"
|
||||
)
|
||||
|
||||
// ImpliedDirectoryMode represents the mode (Unix permissions) applied to directories that are implied by files in a
|
||||
@@ -111,16 +112,6 @@ const (
|
||||
OverlayWhiteoutFormat
|
||||
)
|
||||
|
||||
const (
|
||||
modeISDIR = 040000 // Directory
|
||||
modeISFIFO = 010000 // FIFO
|
||||
modeISREG = 0100000 // Regular file
|
||||
modeISLNK = 0120000 // Symbolic link
|
||||
modeISBLK = 060000 // Block special file
|
||||
modeISCHR = 020000 // Character special file
|
||||
modeISSOCK = 0140000 // Socket
|
||||
)
|
||||
|
||||
// IsArchivePath checks if the (possibly compressed) file at the given path
|
||||
// starts with a tar file header.
|
||||
func IsArchivePath(path string) bool {
|
||||
@@ -469,9 +460,7 @@ func FileInfoHeaderNoLookups(fi os.FileInfo, link string) (*tar.Header, error) {
|
||||
// but is safe to call from a chrooted process. The AccessTime and ChangeTime
|
||||
// fields are not set in the returned header, ModTime is truncated to one-second
|
||||
// precision, and the Uname and Gname fields are only set when fi is a FileInfo
|
||||
// value returned from tar.Header.FileInfo(). Also, regardless of Go version,
|
||||
// this function fills file type bits (e.g. hdr.Mode |= modeISDIR), which have
|
||||
// been deleted since Go 1.9 archive/tar.
|
||||
// value returned from tar.Header.FileInfo().
|
||||
func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, error) {
|
||||
hdr, err := FileInfoHeaderNoLookups(fi, link)
|
||||
if err != nil {
|
||||
@@ -481,36 +470,11 @@ func FileInfoHeader(name string, fi os.FileInfo, link string) (*tar.Header, erro
|
||||
hdr.ModTime = hdr.ModTime.Truncate(time.Second)
|
||||
hdr.AccessTime = time.Time{}
|
||||
hdr.ChangeTime = time.Time{}
|
||||
hdr.Mode = fillGo18FileTypeBits(int64(chmodTarEntry(os.FileMode(hdr.Mode))), fi)
|
||||
hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode)))
|
||||
hdr.Name = canonicalTarName(name, fi.IsDir())
|
||||
return hdr, nil
|
||||
}
|
||||
|
||||
// fillGo18FileTypeBits fills type bits which have been removed on Go 1.9 archive/tar
|
||||
// https://github.com/golang/go/commit/66b5a2f
|
||||
func fillGo18FileTypeBits(mode int64, fi os.FileInfo) int64 {
|
||||
fm := fi.Mode()
|
||||
switch {
|
||||
case fm.IsRegular():
|
||||
mode |= modeISREG
|
||||
case fi.IsDir():
|
||||
mode |= modeISDIR
|
||||
case fm&os.ModeSymlink != 0:
|
||||
mode |= modeISLNK
|
||||
case fm&os.ModeDevice != 0:
|
||||
if fm&os.ModeCharDevice != 0 {
|
||||
mode |= modeISCHR
|
||||
} else {
|
||||
mode |= modeISBLK
|
||||
}
|
||||
case fm&os.ModeNamedPipe != 0:
|
||||
mode |= modeISFIFO
|
||||
case fm&os.ModeSocket != 0:
|
||||
mode |= modeISSOCK
|
||||
}
|
||||
return mode
|
||||
}
|
||||
|
||||
// ReadSecurityXattrToTarHeader reads security.capability xattr from filesystem
|
||||
// to a tar header
|
||||
func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
|
||||
@@ -567,10 +531,17 @@ func newTarAppender(idMapping idtools.IdentityMapping, writer io.Writer, chownOp
|
||||
}
|
||||
}
|
||||
|
||||
// canonicalTarName provides a platform-independent and consistent posix-style
|
||||
// CanonicalTarNameForPath canonicalizes relativePath to a POSIX-style path using
|
||||
// forward slashes. It is an alias for filepath.ToSlash, which is a no-op on
|
||||
// Linux and Unix.
|
||||
func CanonicalTarNameForPath(relativePath string) string {
|
||||
return filepath.ToSlash(relativePath)
|
||||
}
|
||||
|
||||
// canonicalTarName provides a platform-independent and consistent POSIX-style
|
||||
// path for files and directories to be archived regardless of the platform.
|
||||
func canonicalTarName(name string, isDir bool) string {
|
||||
name = CanonicalTarNameForPath(name)
|
||||
name = filepath.ToSlash(name)
|
||||
|
||||
// suffix with '/' for directories
|
||||
if isDir && !strings.HasSuffix(name, "/") {
|
||||
@@ -850,10 +821,29 @@ func Tar(path string, compression Compression) (io.ReadCloser, error) {
|
||||
// TarWithOptions creates an archive from the directory at `path`, only including files whose relative
|
||||
// paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
|
||||
func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {
|
||||
// Fix the source path to work with long path names. This is a no-op
|
||||
// on platforms other than Windows.
|
||||
srcPath = fixVolumePathPrefix(srcPath)
|
||||
tb, err := NewTarballer(srcPath, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go tb.Do()
|
||||
return tb.Reader(), nil
|
||||
}
|
||||
|
||||
// Tarballer is a lower-level interface to TarWithOptions which gives the caller
|
||||
// control over which goroutine the archiving operation executes on.
|
||||
type Tarballer struct {
|
||||
srcPath string
|
||||
options *TarOptions
|
||||
pm *patternmatcher.PatternMatcher
|
||||
pipeReader *io.PipeReader
|
||||
pipeWriter *io.PipeWriter
|
||||
compressWriter io.WriteCloser
|
||||
whiteoutConverter tarWhiteoutConverter
|
||||
}
|
||||
|
||||
// NewTarballer constructs a new tarballer. The arguments are the same as for
|
||||
// TarWithOptions.
|
||||
func NewTarballer(srcPath string, options *TarOptions) (*Tarballer, error) {
|
||||
pm, err := patternmatcher.New(options.ExcludePatterns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -871,183 +861,201 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
ta := newTarAppender(
|
||||
options.IDMap,
|
||||
compressWriter,
|
||||
options.ChownOpts,
|
||||
)
|
||||
ta.WhiteoutConverter = whiteoutConverter
|
||||
return &Tarballer{
|
||||
// Fix the source path to work with long path names. This is a no-op
|
||||
// on platforms other than Windows.
|
||||
srcPath: fixVolumePathPrefix(srcPath),
|
||||
options: options,
|
||||
pm: pm,
|
||||
pipeReader: pipeReader,
|
||||
pipeWriter: pipeWriter,
|
||||
compressWriter: compressWriter,
|
||||
whiteoutConverter: whiteoutConverter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Make sure to check the error on Close.
|
||||
if err := ta.TarWriter.Close(); err != nil {
|
||||
logrus.Errorf("Can't close tar writer: %s", err)
|
||||
}
|
||||
if err := compressWriter.Close(); err != nil {
|
||||
logrus.Errorf("Can't close compress writer: %s", err)
|
||||
}
|
||||
if err := pipeWriter.Close(); err != nil {
|
||||
logrus.Errorf("Can't close pipe writer: %s", err)
|
||||
}
|
||||
}()
|
||||
// Reader returns the reader for the created archive.
|
||||
func (t *Tarballer) Reader() io.ReadCloser {
|
||||
return t.pipeReader
|
||||
}
|
||||
|
||||
// this buffer is needed for the duration of this piped stream
|
||||
defer pools.BufioWriter32KPool.Put(ta.Buffer)
|
||||
// Do performs the archiving operation in the background. The resulting archive
|
||||
// can be read from t.Reader(). Do should only be called once on each Tarballer
|
||||
// instance.
|
||||
func (t *Tarballer) Do() {
|
||||
ta := newTarAppender(
|
||||
t.options.IDMap,
|
||||
t.compressWriter,
|
||||
t.options.ChownOpts,
|
||||
)
|
||||
ta.WhiteoutConverter = t.whiteoutConverter
|
||||
|
||||
// In general we log errors here but ignore them because
|
||||
// during e.g. a diff operation the container can continue
|
||||
// mutating the filesystem and we can see transient errors
|
||||
// from this
|
||||
|
||||
stat, err := os.Lstat(srcPath)
|
||||
if err != nil {
|
||||
return
|
||||
defer func() {
|
||||
// Make sure to check the error on Close.
|
||||
if err := ta.TarWriter.Close(); err != nil {
|
||||
logrus.Errorf("Can't close tar writer: %s", err)
|
||||
}
|
||||
|
||||
if !stat.IsDir() {
|
||||
// We can't later join a non-dir with any includes because the
|
||||
// 'walk' will error if "file/." is stat-ed and "file" is not a
|
||||
// directory. So, we must split the source path and use the
|
||||
// basename as the include.
|
||||
if len(options.IncludeFiles) > 0 {
|
||||
logrus.Warn("Tar: Can't archive a file with includes")
|
||||
}
|
||||
|
||||
dir, base := SplitPathDirEntry(srcPath)
|
||||
srcPath = dir
|
||||
options.IncludeFiles = []string{base}
|
||||
if err := t.compressWriter.Close(); err != nil {
|
||||
logrus.Errorf("Can't close compress writer: %s", err)
|
||||
}
|
||||
|
||||
if len(options.IncludeFiles) == 0 {
|
||||
options.IncludeFiles = []string{"."}
|
||||
}
|
||||
|
||||
seen := make(map[string]bool)
|
||||
|
||||
for _, include := range options.IncludeFiles {
|
||||
rebaseName := options.RebaseNames[include]
|
||||
|
||||
var (
|
||||
parentMatchInfo []patternmatcher.MatchInfo
|
||||
parentDirs []string
|
||||
)
|
||||
|
||||
walkRoot := getWalkRoot(srcPath, include)
|
||||
filepath.Walk(walkRoot, func(filePath string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
logrus.Errorf("Tar: Can't stat file %s to tar: %s", srcPath, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
relFilePath, err := filepath.Rel(srcPath, filePath)
|
||||
if err != nil || (!options.IncludeSourceDir && relFilePath == "." && f.IsDir()) {
|
||||
// Error getting relative path OR we are looking
|
||||
// at the source directory path. Skip in both situations.
|
||||
return nil
|
||||
}
|
||||
|
||||
if options.IncludeSourceDir && include == "." && relFilePath != "." {
|
||||
relFilePath = strings.Join([]string{".", relFilePath}, string(filepath.Separator))
|
||||
}
|
||||
|
||||
skip := false
|
||||
|
||||
// If "include" is an exact match for the current file
|
||||
// then even if there's an "excludePatterns" pattern that
|
||||
// matches it, don't skip it. IOW, assume an explicit 'include'
|
||||
// is asking for that file no matter what - which is true
|
||||
// for some files, like .dockerignore and Dockerfile (sometimes)
|
||||
if include != relFilePath {
|
||||
for len(parentDirs) != 0 {
|
||||
lastParentDir := parentDirs[len(parentDirs)-1]
|
||||
if strings.HasPrefix(relFilePath, lastParentDir+string(os.PathSeparator)) {
|
||||
break
|
||||
}
|
||||
parentDirs = parentDirs[:len(parentDirs)-1]
|
||||
parentMatchInfo = parentMatchInfo[:len(parentMatchInfo)-1]
|
||||
}
|
||||
|
||||
var matchInfo patternmatcher.MatchInfo
|
||||
if len(parentMatchInfo) != 0 {
|
||||
skip, matchInfo, err = pm.MatchesUsingParentResults(relFilePath, parentMatchInfo[len(parentMatchInfo)-1])
|
||||
} else {
|
||||
skip, matchInfo, err = pm.MatchesUsingParentResults(relFilePath, patternmatcher.MatchInfo{})
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Errorf("Error matching %s: %v", relFilePath, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if f.IsDir() {
|
||||
parentDirs = append(parentDirs, relFilePath)
|
||||
parentMatchInfo = append(parentMatchInfo, matchInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if skip {
|
||||
// If we want to skip this file and its a directory
|
||||
// then we should first check to see if there's an
|
||||
// excludes pattern (e.g. !dir/file) that starts with this
|
||||
// dir. If so then we can't skip this dir.
|
||||
|
||||
// Its not a dir then so we can just return/skip.
|
||||
if !f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// No exceptions (!...) in patterns so just skip dir
|
||||
if !pm.Exclusions() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
dirSlash := relFilePath + string(filepath.Separator)
|
||||
|
||||
for _, pat := range pm.Patterns() {
|
||||
if !pat.Exclusion() {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(pat.String()+string(filepath.Separator), dirSlash) {
|
||||
// found a match - so can't skip this dir
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// No matching exclusion dir so just skip dir
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
if seen[relFilePath] {
|
||||
return nil
|
||||
}
|
||||
seen[relFilePath] = true
|
||||
|
||||
// Rename the base resource.
|
||||
if rebaseName != "" {
|
||||
var replacement string
|
||||
if rebaseName != string(filepath.Separator) {
|
||||
// Special case the root directory to replace with an
|
||||
// empty string instead so that we don't end up with
|
||||
// double slashes in the paths.
|
||||
replacement = rebaseName
|
||||
}
|
||||
|
||||
relFilePath = strings.Replace(relFilePath, include, replacement, 1)
|
||||
}
|
||||
|
||||
if err := ta.addTarFile(filePath, relFilePath); err != nil {
|
||||
logrus.Errorf("Can't add file %s to tar: %s", filePath, err)
|
||||
// if pipe is broken, stop writing tar stream to it
|
||||
if err == io.ErrClosedPipe {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err := t.pipeWriter.Close(); err != nil {
|
||||
logrus.Errorf("Can't close pipe writer: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return pipeReader, nil
|
||||
// this buffer is needed for the duration of this piped stream
|
||||
defer pools.BufioWriter32KPool.Put(ta.Buffer)
|
||||
|
||||
// In general we log errors here but ignore them because
|
||||
// during e.g. a diff operation the container can continue
|
||||
// mutating the filesystem and we can see transient errors
|
||||
// from this
|
||||
|
||||
stat, err := os.Lstat(t.srcPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !stat.IsDir() {
|
||||
// We can't later join a non-dir with any includes because the
|
||||
// 'walk' will error if "file/." is stat-ed and "file" is not a
|
||||
// directory. So, we must split the source path and use the
|
||||
// basename as the include.
|
||||
if len(t.options.IncludeFiles) > 0 {
|
||||
logrus.Warn("Tar: Can't archive a file with includes")
|
||||
}
|
||||
|
||||
dir, base := SplitPathDirEntry(t.srcPath)
|
||||
t.srcPath = dir
|
||||
t.options.IncludeFiles = []string{base}
|
||||
}
|
||||
|
||||
if len(t.options.IncludeFiles) == 0 {
|
||||
t.options.IncludeFiles = []string{"."}
|
||||
}
|
||||
|
||||
seen := make(map[string]bool)
|
||||
|
||||
for _, include := range t.options.IncludeFiles {
|
||||
rebaseName := t.options.RebaseNames[include]
|
||||
|
||||
var (
|
||||
parentMatchInfo []patternmatcher.MatchInfo
|
||||
parentDirs []string
|
||||
)
|
||||
|
||||
walkRoot := getWalkRoot(t.srcPath, include)
|
||||
filepath.WalkDir(walkRoot, func(filePath string, f os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
logrus.Errorf("Tar: Can't stat file %s to tar: %s", t.srcPath, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
relFilePath, err := filepath.Rel(t.srcPath, filePath)
|
||||
if err != nil || (!t.options.IncludeSourceDir && relFilePath == "." && f.IsDir()) {
|
||||
// Error getting relative path OR we are looking
|
||||
// at the source directory path. Skip in both situations.
|
||||
return nil
|
||||
}
|
||||
|
||||
if t.options.IncludeSourceDir && include == "." && relFilePath != "." {
|
||||
relFilePath = strings.Join([]string{".", relFilePath}, string(filepath.Separator))
|
||||
}
|
||||
|
||||
skip := false
|
||||
|
||||
// If "include" is an exact match for the current file
|
||||
// then even if there's an "excludePatterns" pattern that
|
||||
// matches it, don't skip it. IOW, assume an explicit 'include'
|
||||
// is asking for that file no matter what - which is true
|
||||
// for some files, like .dockerignore and Dockerfile (sometimes)
|
||||
if include != relFilePath {
|
||||
for len(parentDirs) != 0 {
|
||||
lastParentDir := parentDirs[len(parentDirs)-1]
|
||||
if strings.HasPrefix(relFilePath, lastParentDir+string(os.PathSeparator)) {
|
||||
break
|
||||
}
|
||||
parentDirs = parentDirs[:len(parentDirs)-1]
|
||||
parentMatchInfo = parentMatchInfo[:len(parentMatchInfo)-1]
|
||||
}
|
||||
|
||||
var matchInfo patternmatcher.MatchInfo
|
||||
if len(parentMatchInfo) != 0 {
|
||||
skip, matchInfo, err = t.pm.MatchesUsingParentResults(relFilePath, parentMatchInfo[len(parentMatchInfo)-1])
|
||||
} else {
|
||||
skip, matchInfo, err = t.pm.MatchesUsingParentResults(relFilePath, patternmatcher.MatchInfo{})
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Errorf("Error matching %s: %v", relFilePath, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if f.IsDir() {
|
||||
parentDirs = append(parentDirs, relFilePath)
|
||||
parentMatchInfo = append(parentMatchInfo, matchInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if skip {
|
||||
// If we want to skip this file and its a directory
|
||||
// then we should first check to see if there's an
|
||||
// excludes pattern (e.g. !dir/file) that starts with this
|
||||
// dir. If so then we can't skip this dir.
|
||||
|
||||
// Its not a dir then so we can just return/skip.
|
||||
if !f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// No exceptions (!...) in patterns so just skip dir
|
||||
if !t.pm.Exclusions() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
dirSlash := relFilePath + string(filepath.Separator)
|
||||
|
||||
for _, pat := range t.pm.Patterns() {
|
||||
if !pat.Exclusion() {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(pat.String()+string(filepath.Separator), dirSlash) {
|
||||
// found a match - so can't skip this dir
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// No matching exclusion dir so just skip dir
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
if seen[relFilePath] {
|
||||
return nil
|
||||
}
|
||||
seen[relFilePath] = true
|
||||
|
||||
// Rename the base resource.
|
||||
if rebaseName != "" {
|
||||
var replacement string
|
||||
if rebaseName != string(filepath.Separator) {
|
||||
// Special case the root directory to replace with an
|
||||
// empty string instead so that we don't end up with
|
||||
// double slashes in the paths.
|
||||
replacement = rebaseName
|
||||
}
|
||||
|
||||
relFilePath = strings.Replace(relFilePath, include, replacement, 1)
|
||||
}
|
||||
|
||||
if err := ta.addTarFile(filePath, relFilePath); err != nil {
|
||||
logrus.Errorf("Can't add file %s to tar: %s", filePath, err)
|
||||
// if pipe is broken, stop writing tar stream to it
|
||||
if err == io.ErrClosedPipe {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Unpack unpacks the decompressedArchive to dest with options.
|
||||
|
||||
-8
@@ -35,16 +35,8 @@ func getWalkRoot(srcPath string, include string) string {
|
||||
return strings.TrimSuffix(srcPath, string(filepath.Separator)) + string(filepath.Separator) + include
|
||||
}
|
||||
|
||||
// CanonicalTarNameForPath returns platform-specific filepath
|
||||
// to canonical posix-style path for tar archival. p is relative
|
||||
// path.
|
||||
func CanonicalTarNameForPath(p string) string {
|
||||
return p // already unix-style
|
||||
}
|
||||
|
||||
// chmodTarEntry is used to adjust the file permissions used in tar header based
|
||||
// on the platform the archival is done.
|
||||
|
||||
func chmodTarEntry(perm os.FileMode) os.FileMode {
|
||||
return perm // noop for unix as golang APIs provide perm bits correctly
|
||||
}
|
||||
|
||||
+4
-14
@@ -21,24 +21,14 @@ func getWalkRoot(srcPath string, include string) string {
|
||||
return filepath.Join(srcPath, include)
|
||||
}
|
||||
|
||||
// CanonicalTarNameForPath returns platform-specific filepath
|
||||
// to canonical posix-style path for tar archival. p is relative
|
||||
// path.
|
||||
func CanonicalTarNameForPath(p string) string {
|
||||
return filepath.ToSlash(p)
|
||||
}
|
||||
|
||||
// chmodTarEntry is used to adjust the file permissions used in tar header based
|
||||
// on the platform the archival is done.
|
||||
func chmodTarEntry(perm os.FileMode) os.FileMode {
|
||||
// perm &= 0755 // this 0-ed out tar flags (like link, regular file, directory marker etc.)
|
||||
permPart := perm & os.ModePerm
|
||||
noPermPart := perm &^ os.ModePerm
|
||||
// Add the x bit: make everything +x from windows
|
||||
permPart |= 0111
|
||||
permPart &= 0755
|
||||
// Remove group- and world-writable bits.
|
||||
perm &= 0o755
|
||||
|
||||
return noPermPart | permPart
|
||||
// Add the x bit: make everything +x on Windows
|
||||
return perm | 0o111
|
||||
}
|
||||
|
||||
func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (err error) {
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ func collectFileInfoForChanges(oldDir, newDir string) (*FileInfo, *FileInfo, err
|
||||
func collectFileInfo(sourceDir string) (*FileInfo, error) {
|
||||
root := newRootFileInfo()
|
||||
|
||||
err := filepath.Walk(sourceDir, func(path string, f os.FileInfo, err error) error {
|
||||
err := filepath.WalkDir(sourceDir, func(path string, _ os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+18
-18
@@ -26,23 +26,23 @@ var (
|
||||
// path (from before being processed by utility functions from the path or
|
||||
// filepath stdlib packages) ends with a trailing `/.` or `/`. If the cleaned
|
||||
// path already ends in a `.` path segment, then another is not added. If the
|
||||
// clean path already ends in the separator, then another is not added.
|
||||
func PreserveTrailingDotOrSeparator(cleanedPath string, originalPath string, sep byte) string {
|
||||
// clean path already ends in a path separator, then another is not added.
|
||||
func PreserveTrailingDotOrSeparator(cleanedPath string, originalPath string) string {
|
||||
// Ensure paths are in platform semantics
|
||||
cleanedPath = strings.ReplaceAll(cleanedPath, "/", string(sep))
|
||||
originalPath = strings.ReplaceAll(originalPath, "/", string(sep))
|
||||
cleanedPath = normalizePath(cleanedPath)
|
||||
originalPath = normalizePath(originalPath)
|
||||
|
||||
if !specifiesCurrentDir(cleanedPath) && specifiesCurrentDir(originalPath) {
|
||||
if !hasTrailingPathSeparator(cleanedPath, sep) {
|
||||
if !hasTrailingPathSeparator(cleanedPath) {
|
||||
// Add a separator if it doesn't already end with one (a cleaned
|
||||
// path would only end in a separator if it is the root).
|
||||
cleanedPath += string(sep)
|
||||
cleanedPath += string(filepath.Separator)
|
||||
}
|
||||
cleanedPath += "."
|
||||
}
|
||||
|
||||
if !hasTrailingPathSeparator(cleanedPath, sep) && hasTrailingPathSeparator(originalPath, sep) {
|
||||
cleanedPath += string(sep)
|
||||
if !hasTrailingPathSeparator(cleanedPath) && hasTrailingPathSeparator(originalPath) {
|
||||
cleanedPath += string(filepath.Separator)
|
||||
}
|
||||
|
||||
return cleanedPath
|
||||
@@ -51,14 +51,14 @@ func PreserveTrailingDotOrSeparator(cleanedPath string, originalPath string, sep
|
||||
// assertsDirectory returns whether the given path is
|
||||
// asserted to be a directory, i.e., the path ends with
|
||||
// a trailing '/' or `/.`, assuming a path separator of `/`.
|
||||
func assertsDirectory(path string, sep byte) bool {
|
||||
return hasTrailingPathSeparator(path, sep) || specifiesCurrentDir(path)
|
||||
func assertsDirectory(path string) bool {
|
||||
return hasTrailingPathSeparator(path) || specifiesCurrentDir(path)
|
||||
}
|
||||
|
||||
// hasTrailingPathSeparator returns whether the given
|
||||
// path ends with the system's path separator character.
|
||||
func hasTrailingPathSeparator(path string, sep byte) bool {
|
||||
return len(path) > 0 && path[len(path)-1] == sep
|
||||
func hasTrailingPathSeparator(path string) bool {
|
||||
return len(path) > 0 && path[len(path)-1] == filepath.Separator
|
||||
}
|
||||
|
||||
// specifiesCurrentDir returns whether the given path specifies
|
||||
@@ -285,7 +285,7 @@ func PrepareArchiveCopy(srcContent io.Reader, srcInfo, dstInfo CopyInfo) (dstDir
|
||||
srcBase = srcInfo.RebaseName
|
||||
}
|
||||
return dstDir, RebaseArchiveEntries(srcContent, srcBase, dstBase), nil
|
||||
case assertsDirectory(dstInfo.Path, os.PathSeparator):
|
||||
case assertsDirectory(dstInfo.Path):
|
||||
// The destination does not exist and is asserted to be created as a
|
||||
// directory, but the source content is not a directory. This is an
|
||||
// error condition since you cannot create a directory from a file
|
||||
@@ -386,8 +386,8 @@ func CopyResource(srcPath, dstPath string, followLink bool) error {
|
||||
dstPath = normalizePath(dstPath)
|
||||
|
||||
// Clean the source and destination paths.
|
||||
srcPath = PreserveTrailingDotOrSeparator(filepath.Clean(srcPath), srcPath, os.PathSeparator)
|
||||
dstPath = PreserveTrailingDotOrSeparator(filepath.Clean(dstPath), dstPath, os.PathSeparator)
|
||||
srcPath = PreserveTrailingDotOrSeparator(filepath.Clean(srcPath), srcPath)
|
||||
dstPath = PreserveTrailingDotOrSeparator(filepath.Clean(dstPath), dstPath)
|
||||
|
||||
if srcInfo, err = CopyInfoSourcePath(srcPath, followLink); err != nil {
|
||||
return err
|
||||
@@ -450,7 +450,7 @@ func ResolveHostSourcePath(path string, followLink bool) (resolvedPath, rebaseNa
|
||||
// resolvedDirPath will have been cleaned (no trailing path separators) so
|
||||
// we can manually join it with the base path element.
|
||||
resolvedPath = resolvedDirPath + string(filepath.Separator) + basePath
|
||||
if hasTrailingPathSeparator(path, os.PathSeparator) &&
|
||||
if hasTrailingPathSeparator(path) &&
|
||||
filepath.Base(path) != filepath.Base(resolvedPath) {
|
||||
rebaseName = filepath.Base(path)
|
||||
}
|
||||
@@ -469,8 +469,8 @@ func GetRebaseName(path, resolvedPath string) (string, string) {
|
||||
resolvedPath += string(filepath.Separator) + "."
|
||||
}
|
||||
|
||||
if hasTrailingPathSeparator(path, os.PathSeparator) &&
|
||||
!hasTrailingPathSeparator(resolvedPath, os.PathSeparator) {
|
||||
if hasTrailingPathSeparator(path) &&
|
||||
!hasTrailingPathSeparator(resolvedPath) {
|
||||
resolvedPath += string(filepath.Separator)
|
||||
}
|
||||
|
||||
|
||||
+22
-4
@@ -87,7 +87,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||
basename := filepath.Base(hdr.Name)
|
||||
aufsHardlinks[basename] = hdr
|
||||
if aufsTempdir == "" {
|
||||
if aufsTempdir, err = os.MkdirTemp("", "dockerplnk"); err != nil {
|
||||
if aufsTempdir, err = os.MkdirTemp(dest, "dockerplnk"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer os.RemoveAll(aufsTempdir)
|
||||
@@ -121,7 +121,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
err = filepath.WalkDir(dir, func(path string, info os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = nil // parent was deleted
|
||||
@@ -132,8 +132,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
||||
return nil
|
||||
}
|
||||
if _, exists := unpackedPaths[path]; !exists {
|
||||
err := os.RemoveAll(path)
|
||||
return err
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -224,6 +223,25 @@ func ApplyUncompressedLayer(dest string, layer io.Reader, options *TarOptions) (
|
||||
return applyLayerHandler(dest, layer, options, false)
|
||||
}
|
||||
|
||||
// IsEmpty checks if the tar archive is empty (doesn't contain any entries).
|
||||
func IsEmpty(rd io.Reader) (bool, error) {
|
||||
decompRd, err := DecompressStream(rd)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("failed to decompress archive: %v", err)
|
||||
}
|
||||
defer decompRd.Close()
|
||||
|
||||
tarReader := tar.NewReader(decompRd)
|
||||
if _, err := tarReader.Next(); err != nil {
|
||||
if err == io.EOF {
|
||||
return true, nil
|
||||
}
|
||||
return false, fmt.Errorf("failed to read next archive header: %v", err)
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// do the bulk load of ApplyLayer, but allow for not calling DecompressStream
|
||||
func applyLayerHandler(dest string, layer io.Reader, options *TarOptions, decompress bool) (int64, error) {
|
||||
dest = filepath.Clean(dest)
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package archive
|
||||
|
||||
// CheckSystemDriveAndRemoveDriveLetter verifies that a path, if it includes a drive letter,
|
||||
// is the system drive.
|
||||
// On Linux: this is a no-op.
|
||||
// On Windows: this does the following>
|
||||
// CheckSystemDriveAndRemoveDriveLetter verifies and manipulates a Windows path.
|
||||
// This is used, for example, when validating a user provided path in docker cp.
|
||||
// If a drive letter is supplied, it must be the system drive. The drive letter
|
||||
// is always removed. Also, it translates it to OS semantics (IOW / to \). We
|
||||
// need the path in this syntax so that it can ultimately be concatenated with
|
||||
// a Windows long-path which doesn't support drive-letters. Examples:
|
||||
// C: --> Fail
|
||||
// C:\ --> \
|
||||
// a --> a
|
||||
// /a --> \a
|
||||
// d:\ --> Fail
|
||||
func CheckSystemDriveAndRemoveDriveLetter(path string) (string, error) {
|
||||
return checkSystemDriveAndRemoveDriveLetter(path)
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package archive
|
||||
|
||||
// checkSystemDriveAndRemoveDriveLetter is the non-Windows implementation
|
||||
// of CheckSystemDriveAndRemoveDriveLetter
|
||||
func checkSystemDriveAndRemoveDriveLetter(path string) (string, error) {
|
||||
return path, nil
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package archive
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// checkSystemDriveAndRemoveDriveLetter is the Windows implementation
|
||||
// of CheckSystemDriveAndRemoveDriveLetter
|
||||
func checkSystemDriveAndRemoveDriveLetter(path string) (string, error) {
|
||||
if len(path) == 2 && string(path[1]) == ":" {
|
||||
return "", fmt.Errorf("no relative path specified in %q", path)
|
||||
}
|
||||
if !filepath.IsAbs(path) || len(path) < 2 {
|
||||
return filepath.FromSlash(path), nil
|
||||
}
|
||||
if string(path[1]) == ":" && !strings.EqualFold(string(path[0]), "c") {
|
||||
return "", fmt.Errorf("the specified path is not on the system drive (C:)")
|
||||
}
|
||||
return filepath.FromSlash(path[2:]), nil
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
package fileutils
|
||||
|
||||
import "github.com/moby/patternmatcher"
|
||||
|
||||
type (
|
||||
// PatternMatcher allows checking paths against a list of patterns.
|
||||
//
|
||||
// Deprecated: use github.com/moby/patternmatcher.PatternMatcher
|
||||
PatternMatcher = patternmatcher.PatternMatcher
|
||||
|
||||
// MatchInfo tracks information about parent dir matches while traversing a
|
||||
// filesystem.
|
||||
//
|
||||
// Deprecated: use github.com/moby/patternmatcher.MatchInfo
|
||||
MatchInfo = patternmatcher.MatchInfo
|
||||
|
||||
// Pattern defines a single regexp used to filter file paths.
|
||||
//
|
||||
// Deprecated: use github.com/moby/patternmatcher.Pattern
|
||||
Pattern = patternmatcher.Pattern
|
||||
)
|
||||
|
||||
var (
|
||||
// NewPatternMatcher creates a new matcher object for specific patterns that can
|
||||
// be used later to match against patterns against paths
|
||||
//
|
||||
// Deprecated: use github.com/moby/patternmatcher.New
|
||||
NewPatternMatcher = patternmatcher.New
|
||||
|
||||
// Matches returns true if file matches any of the patterns
|
||||
// and isn't excluded by any of the subsequent patterns.
|
||||
//
|
||||
// This implementation is buggy (it only checks a single parent dir against the
|
||||
// pattern) and will be removed soon. Use MatchesOrParentMatches instead.
|
||||
//
|
||||
// Deprecated: use github.com/moby/patternmatcher.Matches
|
||||
Matches = patternmatcher.Matches
|
||||
|
||||
// MatchesOrParentMatches returns true if file matches any of the patterns
|
||||
// and isn't excluded by any of the subsequent patterns.
|
||||
//
|
||||
// Deprecated: use github.com/moby/patternmatcher.MatchesOrParentMatches
|
||||
MatchesOrParentMatches = patternmatcher.MatchesOrParentMatches
|
||||
)
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
package fileutils // import "github.com/docker/docker/pkg/fileutils"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// CopyFile copies from src to dst until either EOF is reached
|
||||
// on src or an error occurs. It verifies src exists and removes
|
||||
// the dst if it exists.
|
||||
func CopyFile(src, dst string) (int64, error) {
|
||||
cleanSrc := filepath.Clean(src)
|
||||
cleanDst := filepath.Clean(dst)
|
||||
if cleanSrc == cleanDst {
|
||||
return 0, nil
|
||||
}
|
||||
sf, err := os.Open(cleanSrc)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer sf.Close()
|
||||
if err := os.Remove(cleanDst); err != nil && !os.IsNotExist(err) {
|
||||
return 0, err
|
||||
}
|
||||
df, err := os.Create(cleanDst)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer df.Close()
|
||||
return io.Copy(df, sf)
|
||||
}
|
||||
|
||||
// ReadSymlinkedDirectory returns the target directory of a symlink.
|
||||
// The target of the symbolic link may not be a file.
|
||||
func ReadSymlinkedDirectory(path string) (string, error) {
|
||||
var realPath string
|
||||
var err error
|
||||
if realPath, err = filepath.Abs(path); err != nil {
|
||||
return "", fmt.Errorf("unable to get absolute path for %s: %s", path, err)
|
||||
}
|
||||
if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
|
||||
return "", fmt.Errorf("failed to canonicalise path for %s: %s", path, err)
|
||||
}
|
||||
realPathInfo, err := os.Stat(realPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to stat target '%s' of '%s': %s", realPath, path, err)
|
||||
}
|
||||
if !realPathInfo.Mode().IsDir() {
|
||||
return "", fmt.Errorf("canonical path points to a file '%s'", realPath)
|
||||
}
|
||||
return realPath, nil
|
||||
}
|
||||
|
||||
// CreateIfNotExists creates a file or a directory only if it does not already exist.
|
||||
func CreateIfNotExists(path string, isDir bool) error {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if isDir {
|
||||
return os.MkdirAll(path, 0755)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.OpenFile(path, os.O_CREATE, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package fileutils // import "github.com/docker/docker/pkg/fileutils"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetTotalUsedFds returns the number of used File Descriptors by
|
||||
// executing `lsof -p PID`
|
||||
func GetTotalUsedFds() int {
|
||||
pid := os.Getpid()
|
||||
|
||||
cmd := exec.Command("lsof", "-p", strconv.Itoa(pid))
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
outputStr := strings.TrimSpace(string(output))
|
||||
|
||||
fds := strings.Split(outputStr, "\n")
|
||||
|
||||
return len(fds) - 1
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
//go:build linux || freebsd
|
||||
// +build linux freebsd
|
||||
|
||||
package fileutils // import "github.com/docker/docker/pkg/fileutils"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// GetTotalUsedFds Returns the number of used File Descriptors by
|
||||
// reading it via /proc filesystem.
|
||||
func GetTotalUsedFds() int {
|
||||
if fds, err := os.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil {
|
||||
logrus.Errorf("Error opening /proc/%d/fd: %s", os.Getpid(), err)
|
||||
} else {
|
||||
return len(fds)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package fileutils // import "github.com/docker/docker/pkg/fileutils"
|
||||
|
||||
// GetTotalUsedFds Returns the number of used File Descriptors. Not supported
|
||||
// on Windows.
|
||||
func GetTotalUsedFds() int {
|
||||
return -1
|
||||
}
|
||||
+6
-3
@@ -64,13 +64,14 @@ func stick(f string) error {
|
||||
|
||||
// GetDataHome returns XDG_DATA_HOME.
|
||||
// GetDataHome returns $HOME/.local/share and nil error if XDG_DATA_HOME is not set.
|
||||
// If HOME and XDG_DATA_HOME are not set, getpwent(3) is consulted to determine the users home directory.
|
||||
//
|
||||
// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
|
||||
func GetDataHome() (string, error) {
|
||||
if xdgDataHome := os.Getenv("XDG_DATA_HOME"); xdgDataHome != "" {
|
||||
return xdgDataHome, nil
|
||||
}
|
||||
home := os.Getenv("HOME")
|
||||
home := Get()
|
||||
if home == "" {
|
||||
return "", errors.New("could not get either XDG_DATA_HOME or HOME")
|
||||
}
|
||||
@@ -79,13 +80,14 @@ func GetDataHome() (string, error) {
|
||||
|
||||
// GetConfigHome returns XDG_CONFIG_HOME.
|
||||
// GetConfigHome returns $HOME/.config and nil error if XDG_CONFIG_HOME is not set.
|
||||
// If HOME and XDG_CONFIG_HOME are not set, getpwent(3) is consulted to determine the users home directory.
|
||||
//
|
||||
// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
|
||||
func GetConfigHome() (string, error) {
|
||||
if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" {
|
||||
return xdgConfigHome, nil
|
||||
}
|
||||
home := os.Getenv("HOME")
|
||||
home := Get()
|
||||
if home == "" {
|
||||
return "", errors.New("could not get either XDG_CONFIG_HOME or HOME")
|
||||
}
|
||||
@@ -93,8 +95,9 @@ func GetConfigHome() (string, error) {
|
||||
}
|
||||
|
||||
// GetLibHome returns $HOME/.local/lib
|
||||
// If HOME is not set, getpwent(3) is consulted to determine the users home directory.
|
||||
func GetLibHome() (string, error) {
|
||||
home := os.Getenv("HOME")
|
||||
home := Get()
|
||||
if home == "" {
|
||||
return "", errors.New("could not get HOME")
|
||||
}
|
||||
|
||||
-14
@@ -162,20 +162,6 @@ func (i IdentityMapping) Empty() bool {
|
||||
return len(i.UIDMaps) == 0 && len(i.GIDMaps) == 0
|
||||
}
|
||||
|
||||
// UIDs returns the mapping for UID.
|
||||
//
|
||||
// Deprecated: reference the UIDMaps field directly.
|
||||
func (i IdentityMapping) UIDs() []IDMap {
|
||||
return i.UIDMaps
|
||||
}
|
||||
|
||||
// GIDs returns the mapping for GID.
|
||||
//
|
||||
// Deprecated: reference the GIDMaps field directly.
|
||||
func (i IdentityMapping) GIDs() []IDMap {
|
||||
return i.GIDMaps
|
||||
}
|
||||
|
||||
func createIDMap(subidRanges ranges) []IDMap {
|
||||
idMap := []IDMap{}
|
||||
|
||||
|
||||
+26
-66
@@ -14,9 +14,7 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/opencontainers/runc/libcontainer/user"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -25,18 +23,12 @@ var (
|
||||
)
|
||||
|
||||
func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting bool) error {
|
||||
// make an array containing the original path asked for, plus (for mkAll == true)
|
||||
// all path components leading up to the complete path that don't exist before we MkdirAll
|
||||
// so that we can chown all of them properly at the end. If chownExisting is false, we won't
|
||||
// chown the full directory path if it exists
|
||||
|
||||
var paths []string
|
||||
path, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stat, err := system.Stat(path)
|
||||
stat, err := os.Stat(path)
|
||||
if err == nil {
|
||||
if !stat.IsDir() {
|
||||
return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
|
||||
@@ -45,10 +37,15 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting
|
||||
return nil
|
||||
}
|
||||
|
||||
// short-circuit--we were called with an existing directory and chown was requested
|
||||
return setPermissions(path, mode, owner.UID, owner.GID, stat)
|
||||
// short-circuit -- we were called with an existing directory and chown was requested
|
||||
return setPermissions(path, mode, owner, stat)
|
||||
}
|
||||
|
||||
// make an array containing the original path asked for, plus (for mkAll == true)
|
||||
// all path components leading up to the complete path that don't exist before we MkdirAll
|
||||
// so that we can chown all of them properly at the end. If chownExisting is false, we won't
|
||||
// chown the full directory path if it exists
|
||||
var paths []string
|
||||
if os.IsNotExist(err) {
|
||||
paths = []string{path}
|
||||
}
|
||||
@@ -62,54 +59,26 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting
|
||||
if dirPath == "/" {
|
||||
break
|
||||
}
|
||||
if _, err := os.Stat(dirPath); err != nil && os.IsNotExist(err) {
|
||||
if _, err = os.Stat(dirPath); err != nil && os.IsNotExist(err) {
|
||||
paths = append(paths, dirPath)
|
||||
}
|
||||
}
|
||||
if err := system.MkdirAll(path, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := os.Mkdir(path, mode); err != nil && !os.IsExist(err) {
|
||||
if err = os.MkdirAll(path, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err = os.Mkdir(path, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
// even if it existed, we will chown the requested path + any subpaths that
|
||||
// didn't exist when we called MkdirAll
|
||||
for _, pathComponent := range paths {
|
||||
if err := setPermissions(pathComponent, mode, owner.UID, owner.GID, nil); err != nil {
|
||||
if err = setPermissions(pathComponent, mode, owner, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanAccess takes a valid (existing) directory and a uid, gid pair and determines
|
||||
// if that uid, gid pair has access (execute bit) to the directory
|
||||
func CanAccess(path string, pair Identity) bool {
|
||||
statInfo, err := system.Stat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
fileMode := os.FileMode(statInfo.Mode())
|
||||
permBits := fileMode.Perm()
|
||||
return accessible(statInfo.UID() == uint32(pair.UID),
|
||||
statInfo.GID() == uint32(pair.GID), permBits)
|
||||
}
|
||||
|
||||
func accessible(isOwner, isGroup bool, perms os.FileMode) bool {
|
||||
if isOwner && (perms&0100 == 0100) {
|
||||
return true
|
||||
}
|
||||
if isGroup && (perms&0010 == 0010) {
|
||||
return true
|
||||
}
|
||||
if perms&0001 == 0001 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// LookupUser uses traditional local system files lookup (from libcontainer/user) on a username,
|
||||
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs
|
||||
func LookupUser(name string) (user.User, error) {
|
||||
@@ -198,7 +167,10 @@ func callGetent(database, key string) (io.Reader, error) {
|
||||
if getentCmd == "" {
|
||||
return nil, fmt.Errorf("unable to find getent command")
|
||||
}
|
||||
out, err := execCmd(getentCmd, database, key)
|
||||
command := exec.Command(getentCmd, database, key)
|
||||
// we run getent within container filesystem, but without /dev so /dev/null is not available for exec to mock stdin
|
||||
command.Stdin = io.NopCloser(bytes.NewReader(nil))
|
||||
out, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
exitCode, errC := getExitCode(err)
|
||||
if errC != nil {
|
||||
@@ -234,36 +206,24 @@ func getExitCode(err error) (int, error) {
|
||||
// Normally a Chown is a no-op if uid/gid match, but in some cases this can still cause an error, e.g. if the
|
||||
// dir is on an NFS share, so don't call chown unless we absolutely must.
|
||||
// Likewise for setting permissions.
|
||||
func setPermissions(p string, mode os.FileMode, uid, gid int, stat *system.StatT) error {
|
||||
func setPermissions(p string, mode os.FileMode, owner Identity, stat os.FileInfo) error {
|
||||
if stat == nil {
|
||||
var err error
|
||||
stat, err = system.Stat(p)
|
||||
stat, err = os.Stat(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if os.FileMode(stat.Mode()).Perm() != mode.Perm() {
|
||||
if stat.Mode().Perm() != mode.Perm() {
|
||||
if err := os.Chmod(p, mode.Perm()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if stat.UID() == uint32(uid) && stat.GID() == uint32(gid) {
|
||||
ssi := stat.Sys().(*syscall.Stat_t)
|
||||
if ssi.Uid == uint32(owner.UID) && ssi.Gid == uint32(owner.GID) {
|
||||
return nil
|
||||
}
|
||||
return os.Chown(p, uid, gid)
|
||||
}
|
||||
|
||||
// NewIdentityMapping takes a requested username and
|
||||
// using the data from /etc/sub{uid,gid} ranges, creates the
|
||||
// proper uid and gid remapping ranges for that user/group pair
|
||||
//
|
||||
// Deprecated: Use LoadIdentityMapping.
|
||||
func NewIdentityMapping(name string) (*IdentityMapping, error) {
|
||||
m, err := LoadIdentityMapping(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, err
|
||||
return os.Chown(p, owner.UID, owner.GID)
|
||||
}
|
||||
|
||||
// LoadIdentityMapping takes a requested username and
|
||||
@@ -272,7 +232,7 @@ func NewIdentityMapping(name string) (*IdentityMapping, error) {
|
||||
func LoadIdentityMapping(name string) (IdentityMapping, error) {
|
||||
usr, err := LookupUser(name)
|
||||
if err != nil {
|
||||
return IdentityMapping{}, fmt.Errorf("Could not get user for username %s: %v", name, err)
|
||||
return IdentityMapping{}, fmt.Errorf("could not get user for username %s: %v", name, err)
|
||||
}
|
||||
|
||||
subuidRanges, err := lookupSubUIDRanges(usr)
|
||||
@@ -302,7 +262,7 @@ func lookupSubUIDRanges(usr user.User) ([]IDMap, error) {
|
||||
}
|
||||
}
|
||||
if len(rangeList) == 0 {
|
||||
return nil, errors.Errorf("no subuid ranges found for user %q", usr.Name)
|
||||
return nil, fmt.Errorf("no subuid ranges found for user %q", usr.Name)
|
||||
}
|
||||
return createIDMap(rangeList), nil
|
||||
}
|
||||
@@ -319,7 +279,7 @@ func lookupSubGIDRanges(usr user.User) ([]IDMap, error) {
|
||||
}
|
||||
}
|
||||
if len(rangeList) == 0 {
|
||||
return nil, errors.Errorf("no subgid ranges found for user %q", usr.Name)
|
||||
return nil, fmt.Errorf("no subgid ranges found for user %q", usr.Name)
|
||||
}
|
||||
return createIDMap(rangeList), nil
|
||||
}
|
||||
+2
-12
@@ -19,16 +19,6 @@ const (
|
||||
// permissions aren't set through this path, the identity isn't utilized.
|
||||
// Ownership is handled elsewhere, but in the future could be support here
|
||||
// too.
|
||||
func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting bool) error {
|
||||
if err := system.MkdirAll(path, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanAccess takes a valid (existing) directory and a uid, gid pair and determines
|
||||
// if that uid, gid pair has access (execute bit) to the directory
|
||||
// Windows does not require/support this function, so always return true
|
||||
func CanAccess(path string, identity Identity) bool {
|
||||
return true
|
||||
func mkdirAs(path string, _ os.FileMode, _ Identity, _, _ bool) error {
|
||||
return system.MkdirAll(path, 0)
|
||||
}
|
||||
+20
-17
@@ -2,6 +2,7 @@ package idtools // import "github.com/docker/docker/pkg/idtools"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -32,21 +33,21 @@ const (
|
||||
// mapping ranges in containers.
|
||||
func AddNamespaceRangesUser(name string) (int, int, error) {
|
||||
if err := addUser(name); err != nil {
|
||||
return -1, -1, fmt.Errorf("Error adding user %q: %v", name, err)
|
||||
return -1, -1, fmt.Errorf("error adding user %q: %v", name, err)
|
||||
}
|
||||
|
||||
// Query the system for the created uid and gid pair
|
||||
out, err := execCmd("id", name)
|
||||
out, err := exec.Command("id", name).CombinedOutput()
|
||||
if err != nil {
|
||||
return -1, -1, fmt.Errorf("Error trying to find uid/gid for new user %q: %v", name, err)
|
||||
return -1, -1, fmt.Errorf("error trying to find uid/gid for new user %q: %v", name, err)
|
||||
}
|
||||
matches := idOutRegexp.FindStringSubmatch(strings.TrimSpace(string(out)))
|
||||
if len(matches) != 3 {
|
||||
return -1, -1, fmt.Errorf("Can't find uid, gid from `id` output: %q", string(out))
|
||||
return -1, -1, fmt.Errorf("can't find uid, gid from `id` output: %q", string(out))
|
||||
}
|
||||
uid, err := strconv.Atoi(matches[1])
|
||||
if err != nil {
|
||||
return -1, -1, fmt.Errorf("Can't convert found uid (%s) to int: %v", matches[1], err)
|
||||
return -1, -1, fmt.Errorf("can't convert found uid (%s) to int: %v", matches[1], err)
|
||||
}
|
||||
gid, err := strconv.Atoi(matches[2])
|
||||
if err != nil {
|
||||
@@ -57,7 +58,7 @@ func AddNamespaceRangesUser(name string) (int, int, error) {
|
||||
// do not get auto-created ranges in subuid/subgid)
|
||||
|
||||
if err := createSubordinateRanges(name); err != nil {
|
||||
return -1, -1, fmt.Errorf("Couldn't create subordinate ID ranges: %v", err)
|
||||
return -1, -1, fmt.Errorf("couldn't create subordinate ID ranges: %v", err)
|
||||
}
|
||||
return uid, gid, nil
|
||||
}
|
||||
@@ -81,7 +82,7 @@ func addUser(name string) error {
|
||||
return fmt.Errorf("cannot add user; no useradd/adduser binary found")
|
||||
}
|
||||
|
||||
if out, err := execCmd(userCommand, args...); err != nil {
|
||||
if out, err := exec.Command(userCommand, args...).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to add user with error: %v; output: %q", err, string(out))
|
||||
}
|
||||
return nil
|
||||
@@ -92,33 +93,35 @@ func createSubordinateRanges(name string) error {
|
||||
// by the distro tooling
|
||||
ranges, err := parseSubuid(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error while looking for subuid ranges for user %q: %v", name, err)
|
||||
return fmt.Errorf("error while looking for subuid ranges for user %q: %v", name, err)
|
||||
}
|
||||
if len(ranges) == 0 {
|
||||
// no UID ranges; let's create one
|
||||
startID, err := findNextUIDRange()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Can't find available subuid range: %v", err)
|
||||
return fmt.Errorf("can't find available subuid range: %v", err)
|
||||
}
|
||||
out, err := execCmd("usermod", "-v", fmt.Sprintf("%d-%d", startID, startID+defaultRangeLen-1), name)
|
||||
idRange := fmt.Sprintf("%d-%d", startID, startID+defaultRangeLen-1)
|
||||
out, err := exec.Command("usermod", "-v", idRange, name).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to add subuid range to user: %q; output: %s, err: %v", name, out, err)
|
||||
return fmt.Errorf("unable to add subuid range to user: %q; output: %s, err: %v", name, out, err)
|
||||
}
|
||||
}
|
||||
|
||||
ranges, err = parseSubgid(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error while looking for subgid ranges for user %q: %v", name, err)
|
||||
return fmt.Errorf("error while looking for subgid ranges for user %q: %v", name, err)
|
||||
}
|
||||
if len(ranges) == 0 {
|
||||
// no GID ranges; let's create one
|
||||
startID, err := findNextGIDRange()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Can't find available subgid range: %v", err)
|
||||
return fmt.Errorf("can't find available subgid range: %v", err)
|
||||
}
|
||||
out, err := execCmd("usermod", "-w", fmt.Sprintf("%d-%d", startID, startID+defaultRangeLen-1), name)
|
||||
idRange := fmt.Sprintf("%d-%d", startID, startID+defaultRangeLen-1)
|
||||
out, err := exec.Command("usermod", "-w", idRange, name).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to add subgid range to user: %q; output: %s, err: %v", name, out, err)
|
||||
return fmt.Errorf("unable to add subgid range to user: %q; output: %s, err: %v", name, out, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -127,7 +130,7 @@ func createSubordinateRanges(name string) error {
|
||||
func findNextUIDRange() (int, error) {
|
||||
ranges, err := parseSubuid("ALL")
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("Couldn't parse all ranges in /etc/subuid file: %v", err)
|
||||
return -1, fmt.Errorf("couldn't parse all ranges in /etc/subuid file: %v", err)
|
||||
}
|
||||
sort.Sort(ranges)
|
||||
return findNextRangeStart(ranges)
|
||||
@@ -136,7 +139,7 @@ func findNextUIDRange() (int, error) {
|
||||
func findNextGIDRange() (int, error) {
|
||||
ranges, err := parseSubgid("ALL")
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("Couldn't parse all ranges in /etc/subgid file: %v", err)
|
||||
return -1, fmt.Errorf("couldn't parse all ranges in /etc/subgid file: %v", err)
|
||||
}
|
||||
sort.Sort(ranges)
|
||||
return findNextRangeStart(ranges)
|
||||
|
||||
-5
@@ -25,8 +25,3 @@ func resolveBinary(binname string) (string, error) {
|
||||
}
|
||||
return "", fmt.Errorf("Binary %q does not resolve to a binary of that name in $PATH (%q)", binname, resolvedPath)
|
||||
}
|
||||
|
||||
func execCmd(cmd string, arg ...string) ([]byte, error) {
|
||||
execCmd := exec.Command(cmd, arg...)
|
||||
return execCmd.CombinedOutput()
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package ioutils // import "github.com/docker/docker/pkg/ioutils"
|
||||
|
||||
import "os"
|
||||
|
||||
// TempDir on Unix systems is equivalent to os.MkdirTemp.
|
||||
func TempDir(dir, prefix string) (string, error) {
|
||||
return os.MkdirTemp(dir, prefix)
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package ioutils // import "github.com/docker/docker/pkg/ioutils"
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/docker/docker/pkg/longpath"
|
||||
)
|
||||
|
||||
// TempDir is the equivalent of os.MkdirTemp, except that the result is in Windows longpath format.
|
||||
func TempDir(dir, prefix string) (string, error) {
|
||||
tempDir, err := os.MkdirTemp(dir, prefix)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return longpath.AddPrefix(tempDir), nil
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package ioutils
|
||||
|
||||
import "github.com/docker/docker/pkg/longpath"
|
||||
|
||||
// TempDir is the equivalent of [os.MkdirTemp], except that on Windows
|
||||
// the result is in Windows longpath format. On Unix systems it is
|
||||
// equivalent to [os.MkdirTemp].
|
||||
//
|
||||
// Deprecated: use [longpath.MkdirTemp].
|
||||
var TempDir = longpath.MkdirTemp
|
||||
Loaded 100 of 368 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user