mirror of
https://github.com/moby/moby.git
synced 2026-01-13 11:42:02 +00:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e96db1c32 | ||
|
|
c21fe3efa3 | ||
|
|
86af4eddb3 | ||
|
|
73511cdee0 | ||
|
|
9326cda7bf | ||
|
|
76fcf9a8e0 | ||
|
|
ef1912d8b6 | ||
|
|
10739af81a | ||
|
|
ac2de55998 | ||
|
|
9a2b531127 | ||
|
|
2f5bbbe16b | ||
|
|
40618081f1 | ||
|
|
21da192ae4 | ||
|
|
2c91196921 | ||
|
|
a9a8787c93 | ||
|
|
c9689eccf5 | ||
|
|
c8af8ebe4a | ||
|
|
7d95fe8db5 | ||
|
|
801fd16e3e | ||
|
|
6667e96dad | ||
|
|
ee8b788538 | ||
|
|
96c9353e9b | ||
|
|
ab570ab3d6 | ||
|
|
7532420f3b | ||
|
|
a4d5b6b4d0 | ||
|
|
e829cca0ee | ||
|
|
82d8f8d6e6 | ||
|
|
6c68be24a2 | ||
|
|
b7c059886c | ||
|
|
745e2356ab | ||
|
|
29f24a828b | ||
|
|
0d6a1a212b | ||
|
|
f5209d23a8 | ||
|
|
442f29a699 | ||
|
|
f07644e17e | ||
|
|
d3c051318f | ||
|
|
5368c3a04f | ||
|
|
8d5d655db0 | ||
|
|
f51e18f58e | ||
|
|
57dd56726a | ||
|
|
f9dfd139ec | ||
|
|
051d587447 | ||
|
|
9954d7c6bd | ||
|
|
cd7240f6d9 | ||
|
|
17b8631545 | ||
|
|
8383c487c6 | ||
|
|
7a54a16740 | ||
|
|
2fabb28813 | ||
|
|
5ae5969739 | ||
|
|
3485cfbb1e | ||
|
|
3b81ca4969 | ||
|
|
80572929e1 | ||
|
|
fb92caf2aa | ||
|
|
61269e718f | ||
|
|
d25b0bd7ea | ||
|
|
d8b768149b | ||
|
|
6d30487d2e | ||
|
|
329d403e20 | ||
|
|
d66589496e | ||
|
|
e1ca74361b | ||
|
|
cf933115b6 | ||
|
|
9fa76786ab | ||
|
|
9160b9fda6 | ||
|
|
8599f2a3fb | ||
|
|
0a48d26fbc | ||
|
|
330a6f959f | ||
|
|
c4689034fd | ||
|
|
4eed3dcdfe | ||
|
|
c187f95fe1 | ||
|
|
a33b302d54 | ||
|
|
484480f56a | ||
|
|
aff003139c | ||
|
|
1014f481de | ||
|
|
d57b899904 | ||
|
|
fde80fe2e7 | ||
|
|
bfdb8918f9 | ||
|
|
83ae9927fb | ||
|
|
14a8fac092 | ||
|
|
59c5059081 | ||
|
|
1552e30a05 | ||
|
|
c64314fd55 | ||
|
|
61e2199b78 | ||
|
|
ea72f9f72c | ||
|
|
f696e0d2a7 | ||
|
|
7ebd88d2d9 | ||
|
|
4f30a930ad | ||
|
|
7b0ab1011c |
@@ -51,6 +51,12 @@ linters-settings:
|
||||
deny:
|
||||
- pkg: io/ioutil
|
||||
desc: The io/ioutil package has been deprecated, see https://go.dev/doc/go1.16#ioutil
|
||||
- pkg: "github.com/stretchr/testify/assert"
|
||||
desc: Use "gotest.tools/v3/assert" instead
|
||||
- pkg: "github.com/stretchr/testify/require"
|
||||
desc: Use "gotest.tools/v3/assert" instead
|
||||
- pkg: "github.com/stretchr/testify/suite"
|
||||
desc: Do not use
|
||||
revive:
|
||||
rules:
|
||||
# FIXME make sure all packages have a description. Currently, there's many packages without.
|
||||
|
||||
6
.mailmap
6
.mailmap
@@ -173,6 +173,8 @@ Dattatraya Kumbhar <dattatraya.kumbhar@gslab.com>
|
||||
Dave Goodchild <buddhamagnet@gmail.com>
|
||||
Dave Henderson <dhenderson@gmail.com> <Dave.Henderson@ca.ibm.com>
|
||||
Dave Tucker <dt@docker.com> <dave@dtucker.co.uk>
|
||||
David Dooling <dooling@gmail.com>
|
||||
David Dooling <dooling@gmail.com> <david.dooling@docker.com>
|
||||
David M. Karr <davidmichaelkarr@gmail.com>
|
||||
David Sheets <dsheets@docker.com> <sheets@alum.mit.edu>
|
||||
David Sissitka <me@dsissitka.com>
|
||||
@@ -219,6 +221,8 @@ Felix Hupfeld <felix@quobyte.com> <quofelix@users.noreply.github.com>
|
||||
Felix Ruess <felix.ruess@gmail.com> <felix.ruess@roboception.de>
|
||||
Feng Yan <fy2462@gmail.com>
|
||||
Fengtu Wang <wangfengtu@huawei.com> <wangfengtu@huawei.com>
|
||||
Filipe Pina <hzlu1ot0@duck.com>
|
||||
Filipe Pina <hzlu1ot0@duck.com> <636320+fopina@users.noreply.github.com>
|
||||
Francisco Carriedo <fcarriedo@gmail.com>
|
||||
Frank Rosquin <frank.rosquin+github@gmail.com> <frank.rosquin@gmail.com>
|
||||
Frank Yang <yyb196@gmail.com>
|
||||
@@ -270,6 +274,7 @@ Hollie Teal <hollie@docker.com> <hollie.teal@docker.com>
|
||||
Hollie Teal <hollie@docker.com> <hollietealok@users.noreply.github.com>
|
||||
hsinko <21551195@zju.edu.cn> <hsinko@users.noreply.github.com>
|
||||
Hu Keping <hukeping@huawei.com>
|
||||
Huajin Tong <fliterdashen@gmail.com>
|
||||
Hui Kang <hkang.sunysb@gmail.com>
|
||||
Hui Kang <hkang.sunysb@gmail.com> <kangh@us.ibm.com>
|
||||
Huu Nguyen <huu@prismskylabs.com> <whoshuu@gmail.com>
|
||||
@@ -563,6 +568,7 @@ Sebastiaan van Stijn <github@gone.nl> <sebastiaan@ws-key-sebas3.dpi1.dpi>
|
||||
Sebastiaan van Stijn <github@gone.nl> <thaJeztah@users.noreply.github.com>
|
||||
Sebastian Thomschke <sebthom@users.noreply.github.com>
|
||||
Seongyeol Lim <seongyeol37@gmail.com>
|
||||
Serhii Nakon <serhii.n@thescimus.com>
|
||||
Shaun Kaasten <shaunk@gmail.com>
|
||||
Shawn Landden <shawn@churchofgit.com> <shawnlandden@gmail.com>
|
||||
Shengbo Song <thomassong@tencent.com>
|
||||
|
||||
9
AUTHORS
9
AUTHORS
@@ -669,6 +669,7 @@ Erik Hollensbe <github@hollensbe.org>
|
||||
Erik Inge Bolsø <knan@redpill-linpro.com>
|
||||
Erik Kristensen <erik@erikkristensen.com>
|
||||
Erik Sipsma <erik@sipsma.dev>
|
||||
Erik Sjölund <erik.sjolund@gmail.com>
|
||||
Erik St. Martin <alakriti@gmail.com>
|
||||
Erik Weathers <erikdw@gmail.com>
|
||||
Erno Hopearuoho <erno.hopearuoho@gmail.com>
|
||||
@@ -731,6 +732,7 @@ Feroz Salam <feroz.salam@sourcegraph.com>
|
||||
Ferran Rodenas <frodenas@gmail.com>
|
||||
Filipe Brandenburger <filbranden@google.com>
|
||||
Filipe Oliveira <contato@fmoliveira.com.br>
|
||||
Filipe Pina <hzlu1ot0@duck.com>
|
||||
Flavio Castelli <fcastelli@suse.com>
|
||||
Flavio Crisciani <flavio.crisciani@docker.com>
|
||||
Florian <FWirtz@users.noreply.github.com>
|
||||
@@ -875,6 +877,8 @@ Hsing-Yu (David) Chen <davidhsingyuchen@gmail.com>
|
||||
hsinko <21551195@zju.edu.cn>
|
||||
Hu Keping <hukeping@huawei.com>
|
||||
Hu Tao <hutao@cn.fujitsu.com>
|
||||
Huajin Tong <fliterdashen@gmail.com>
|
||||
huang-jl <1046678590@qq.com>
|
||||
HuanHuan Ye <logindaveye@gmail.com>
|
||||
Huanzhong Zhang <zhanghuanzhong90@gmail.com>
|
||||
Huayi Zhang <irachex@gmail.com>
|
||||
@@ -969,6 +973,7 @@ Jannick Fahlbusch <git@jf-projects.de>
|
||||
Januar Wayong <januar@gmail.com>
|
||||
Jared Biel <jared.biel@bolderthinking.com>
|
||||
Jared Hocutt <jaredh@netapp.com>
|
||||
Jaroslav Jindrak <dzejrou@gmail.com>
|
||||
Jaroslaw Zabiello <hipertracker@gmail.com>
|
||||
Jasmine Hegman <jasmine@jhegman.com>
|
||||
Jason A. Donenfeld <Jason@zx2c4.com>
|
||||
@@ -1012,6 +1017,7 @@ Jeffrey Bolle <jeffreybolle@gmail.com>
|
||||
Jeffrey Morgan <jmorganca@gmail.com>
|
||||
Jeffrey van Gogh <jvg@google.com>
|
||||
Jenny Gebske <jennifer@gebske.de>
|
||||
Jeongseok Kang <piono623@naver.com>
|
||||
Jeremy Chambers <jeremy@thehipbot.com>
|
||||
Jeremy Grosser <jeremy@synack.me>
|
||||
Jeremy Huntwork <jhuntwork@lightcubesolutions.com>
|
||||
@@ -1029,6 +1035,7 @@ Jezeniel Zapanta <jpzapanta22@gmail.com>
|
||||
Jhon Honce <jhonce@redhat.com>
|
||||
Ji.Zhilong <zhilongji@gmail.com>
|
||||
Jian Liao <jliao@alauda.io>
|
||||
Jian Zeng <anonymousknight96@gmail.com>
|
||||
Jian Zhang <zhangjian.fnst@cn.fujitsu.com>
|
||||
Jiang Jinyang <jjyruby@gmail.com>
|
||||
Jianyong Wu <jianyong.wu@arm.com>
|
||||
@@ -1967,6 +1974,7 @@ Sergey Evstifeev <sergey.evstifeev@gmail.com>
|
||||
Sergii Kabashniuk <skabashnyuk@codenvy.com>
|
||||
Sergio Lopez <slp@redhat.com>
|
||||
Serhat Gülçiçek <serhat25@gmail.com>
|
||||
Serhii Nakon <serhii.n@thescimus.com>
|
||||
SeungUkLee <lsy931106@gmail.com>
|
||||
Sevki Hasirci <s@sevki.org>
|
||||
Shane Canon <scanon@lbl.gov>
|
||||
@@ -2253,6 +2261,7 @@ VladimirAus <v_roudakov@yahoo.com>
|
||||
Vladislav Kolesnikov <vkolesnikov@beget.ru>
|
||||
Vlastimil Zeman <vlastimil.zeman@diffblue.com>
|
||||
Vojtech Vitek (V-Teq) <vvitek@redhat.com>
|
||||
voloder <110066198+voloder@users.noreply.github.com>
|
||||
Walter Leibbrandt <github@wrl.co.za>
|
||||
Walter Stanish <walter@pratyeka.org>
|
||||
Wang Chao <chao.wang@ucloud.cn>
|
||||
|
||||
@@ -101,7 +101,7 @@ the contributors guide.
|
||||
<td>
|
||||
<p>
|
||||
Register for the Docker Community Slack at
|
||||
<a href="https://dockr.ly/slack" target="_blank">https://dockr.ly/slack</a>.
|
||||
<a href="https://dockr.ly/comm-slack" target="_blank">https://dockr.ly/comm-slack</a>.
|
||||
We use the #moby-project channel for general discussion, and there are separate channels for other Moby projects such as #containerd.
|
||||
</p>
|
||||
</td>
|
||||
|
||||
22
Dockerfile
22
Dockerfile
@@ -1,4 +1,4 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
ARG GO_VERSION=1.21.10
|
||||
ARG BASE_DEBIAN_DISTRO="bookworm"
|
||||
@@ -24,6 +24,12 @@ ARG DOCKER_STATIC=1
|
||||
# specified here should match a current release.
|
||||
ARG REGISTRY_VERSION=2.8.3
|
||||
|
||||
# delve is currently only supported on linux/amd64 and linux/arm64;
|
||||
# https://github.com/go-delve/delve/blob/v1.8.1/pkg/proc/native/support_sentinel.go#L1-L6
|
||||
ARG DELVE_SUPPORTED=${TARGETPLATFORM#linux/amd64} DELVE_SUPPORTED=${DELVE_SUPPORTED#linux/arm64}
|
||||
ARG DELVE_SUPPORTED=${DELVE_SUPPORTED:+"unsupported"}
|
||||
ARG DELVE_SUPPORTED=${DELVE_SUPPORTED:-"supported"}
|
||||
|
||||
# cross compilation helper
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
@@ -144,7 +150,7 @@ RUN git init . && git remote add origin "https://github.com/go-delve/delve.git"
|
||||
ARG DELVE_VERSION=v1.21.1
|
||||
RUN git fetch -q --depth 1 origin "${DELVE_VERSION}" +refs/tags/*:refs/tags/* && git checkout -q FETCH_HEAD
|
||||
|
||||
FROM base AS delve-build
|
||||
FROM base AS delve-supported
|
||||
WORKDIR /usr/src/delve
|
||||
ARG TARGETPLATFORM
|
||||
RUN --mount=from=delve-src,src=/usr/src/delve,rw \
|
||||
@@ -155,16 +161,8 @@ RUN --mount=from=delve-src,src=/usr/src/delve,rw \
|
||||
xx-verify /build/dlv
|
||||
EOT
|
||||
|
||||
# delve is currently only supported on linux/amd64 and linux/arm64;
|
||||
# https://github.com/go-delve/delve/blob/v1.8.1/pkg/proc/native/support_sentinel.go#L1-L6
|
||||
FROM binary-dummy AS delve-windows
|
||||
FROM binary-dummy AS delve-linux-arm
|
||||
FROM binary-dummy AS delve-linux-ppc64le
|
||||
FROM binary-dummy AS delve-linux-s390x
|
||||
FROM delve-build AS delve-linux-amd64
|
||||
FROM delve-build AS delve-linux-arm64
|
||||
FROM delve-linux-${TARGETARCH} AS delve-linux
|
||||
FROM delve-${TARGETOS} AS delve
|
||||
FROM binary-dummy AS delve-unsupported
|
||||
FROM delve-${DELVE_SUPPORTED} AS delve
|
||||
|
||||
FROM base AS tomll
|
||||
# GOTOML_VERSION specifies the version of the tomll binary to build and install
|
||||
|
||||
@@ -456,15 +456,27 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
|
||||
if hostConfig == nil {
|
||||
hostConfig = &container.HostConfig{}
|
||||
}
|
||||
if hostConfig.NetworkMode == "" {
|
||||
hostConfig.NetworkMode = "default"
|
||||
}
|
||||
if networkingConfig == nil {
|
||||
networkingConfig = &network.NetworkingConfig{}
|
||||
}
|
||||
if networkingConfig.EndpointsConfig == nil {
|
||||
networkingConfig.EndpointsConfig = make(map[string]*network.EndpointSettings)
|
||||
}
|
||||
// The NetworkMode "default" is used as a way to express a container should
|
||||
// be attached to the OS-dependant default network, in an OS-independent
|
||||
// way. Doing this conversion as soon as possible ensures we have less
|
||||
// NetworkMode to handle down the path (including in the
|
||||
// backward-compatibility layer we have just below).
|
||||
//
|
||||
// Note that this is not the only place where this conversion has to be
|
||||
// done (as there are various other places where containers get created).
|
||||
if hostConfig.NetworkMode == "" || hostConfig.NetworkMode.IsDefault() {
|
||||
hostConfig.NetworkMode = runconfig.DefaultDaemonNetworkMode()
|
||||
if nw, ok := networkingConfig.EndpointsConfig[network.NetworkDefault]; ok {
|
||||
networkingConfig.EndpointsConfig[hostConfig.NetworkMode.NetworkName()] = nw
|
||||
delete(networkingConfig.EndpointsConfig, network.NetworkDefault)
|
||||
}
|
||||
}
|
||||
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
|
||||
@@ -646,7 +658,7 @@ func handleMACAddressBC(config *container.Config, hostConfig *container.HostConf
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
if !hostConfig.NetworkMode.IsDefault() && !hostConfig.NetworkMode.IsBridge() && !hostConfig.NetworkMode.IsUserDefined() {
|
||||
if !hostConfig.NetworkMode.IsBridge() && !hostConfig.NetworkMode.IsUserDefined() {
|
||||
return "", runconfig.ErrConflictContainerNetworkAndMac
|
||||
}
|
||||
|
||||
@@ -675,7 +687,7 @@ func handleMACAddressBC(config *container.Config, hostConfig *container.HostConf
|
||||
return "", nil
|
||||
}
|
||||
var warning string
|
||||
if hostConfig.NetworkMode.IsDefault() || hostConfig.NetworkMode.IsBridge() || hostConfig.NetworkMode.IsUserDefined() {
|
||||
if hostConfig.NetworkMode.IsBridge() || hostConfig.NetworkMode.IsUserDefined() {
|
||||
nwName := hostConfig.NetworkMode.NetworkName()
|
||||
// If there's no endpoint config, create a place to store the configured address.
|
||||
if len(networkingConfig.EndpointsConfig) == 0 {
|
||||
|
||||
@@ -22,7 +22,7 @@ func normalizeWorkdir(_ string, current string, requested string) (string, error
|
||||
if !filepath.IsAbs(requested) {
|
||||
return filepath.Join(string(os.PathSeparator), current, requested), nil
|
||||
}
|
||||
return requested, nil
|
||||
return filepath.Clean(requested), nil
|
||||
}
|
||||
|
||||
// resolveCmdLine takes a command line arg set and optionally prepends a platform-specific
|
||||
|
||||
@@ -15,11 +15,13 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/go-connections/nat"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
@@ -377,12 +379,21 @@ func hostConfigFromOptions(options *types.ImageBuildOptions) *container.HostConf
|
||||
Ulimits: options.Ulimits,
|
||||
}
|
||||
|
||||
// We need to make sure no empty string or "default" NetworkMode is
|
||||
// provided to the daemon as it doesn't support them.
|
||||
//
|
||||
// This is in line with what the ContainerCreate API endpoint does.
|
||||
networkMode := options.NetworkMode
|
||||
if networkMode == "" || networkMode == network.NetworkDefault {
|
||||
networkMode = runconfig.DefaultDaemonNetworkMode().NetworkName()
|
||||
}
|
||||
|
||||
hc := &container.HostConfig{
|
||||
SecurityOpt: options.SecurityOpt,
|
||||
Isolation: options.Isolation,
|
||||
ShmSize: options.ShmSize,
|
||||
Resources: resources,
|
||||
NetworkMode: container.NetworkMode(options.NetworkMode),
|
||||
NetworkMode: container.NetworkMode(networkMode),
|
||||
// Set a log config to override any default value set on the daemon
|
||||
LogConfig: defaultLogConfig,
|
||||
ExtraHosts: options.ExtraHosts,
|
||||
|
||||
@@ -299,8 +299,8 @@ func (container *Container) SetupWorkingDirectory(rootIdentity idtools.Identity)
|
||||
return nil
|
||||
}
|
||||
|
||||
container.Config.WorkingDir = filepath.Clean(container.Config.WorkingDir)
|
||||
pth, err := container.GetResourcePath(container.Config.WorkingDir)
|
||||
workdir := filepath.Clean(container.Config.WorkingDir)
|
||||
pth, err := container.GetResourcePath(workdir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -120,8 +120,9 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
|
||||
|
||||
if m.VolumeOptions != nil {
|
||||
mount.VolumeOptions = &mounttypes.VolumeOptions{
|
||||
NoCopy: m.VolumeOptions.NoCopy,
|
||||
Labels: m.VolumeOptions.Labels,
|
||||
NoCopy: m.VolumeOptions.NoCopy,
|
||||
Labels: m.VolumeOptions.Labels,
|
||||
Subpath: m.VolumeOptions.Subpath,
|
||||
}
|
||||
if m.VolumeOptions.DriverConfig != nil {
|
||||
mount.VolumeOptions.DriverConfig = &mounttypes.Driver{
|
||||
@@ -406,8 +407,9 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
|
||||
|
||||
if m.VolumeOptions != nil {
|
||||
mount.VolumeOptions = &swarmapi.Mount_VolumeOptions{
|
||||
NoCopy: m.VolumeOptions.NoCopy,
|
||||
Labels: m.VolumeOptions.Labels,
|
||||
NoCopy: m.VolumeOptions.NoCopy,
|
||||
Labels: m.VolumeOptions.Labels,
|
||||
Subpath: m.VolumeOptions.Subpath,
|
||||
}
|
||||
if m.VolumeOptions.DriverConfig != nil {
|
||||
mount.VolumeOptions.DriverConfig = &swarmapi.Driver{
|
||||
|
||||
42
daemon/cluster/convert/pluginadapter.go
Normal file
42
daemon/cluster/convert/pluginadapter.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/pkg/plugingetter"
|
||||
"github.com/moby/swarmkit/v2/node/plugin"
|
||||
)
|
||||
|
||||
// SwarmPluginGetter adapts a plugingetter.PluginGetter to a Swarmkit plugin.Getter.
|
||||
func SwarmPluginGetter(pg plugingetter.PluginGetter) plugin.Getter {
|
||||
return pluginGetter{pg}
|
||||
}
|
||||
|
||||
type pluginGetter struct {
|
||||
pg plugingetter.PluginGetter
|
||||
}
|
||||
|
||||
var _ plugin.Getter = (*pluginGetter)(nil)
|
||||
|
||||
type swarmPlugin struct {
|
||||
plugingetter.CompatPlugin
|
||||
}
|
||||
|
||||
func (p swarmPlugin) Client() plugin.Client {
|
||||
return p.CompatPlugin.Client()
|
||||
}
|
||||
|
||||
func (g pluginGetter) Get(name string, capability string) (plugin.Plugin, error) {
|
||||
p, err := g.pg.Get(name, capability, plugingetter.Lookup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return swarmPlugin{p}, nil
|
||||
}
|
||||
|
||||
func (g pluginGetter) GetAllManagedPluginsByCap(capability string) []plugin.Plugin {
|
||||
pp := g.pg.GetAllManagedPluginsByCap(capability)
|
||||
ret := make([]plugin.Plugin, len(pp))
|
||||
for i, p := range pp {
|
||||
ret[i] = swarmPlugin{p}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -4,11 +4,13 @@ import (
|
||||
"testing"
|
||||
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/api/types/swarm/runtime"
|
||||
google_protobuf3 "github.com/gogo/protobuf/types"
|
||||
swarmapi "github.com/moby/swarmkit/v2/api"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestServiceConvertFromGRPCRuntimeContainer(t *testing.T) {
|
||||
@@ -611,3 +613,32 @@ func TestServiceConvertToGRPCConfigs(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceConvertToGRPCVolumeSubpath(t *testing.T) {
|
||||
s := swarmtypes.ServiceSpec{
|
||||
TaskTemplate: swarmtypes.TaskSpec{
|
||||
ContainerSpec: &swarmtypes.ContainerSpec{
|
||||
Mounts: []mount.Mount{
|
||||
{
|
||||
Source: "/foo/bar",
|
||||
Target: "/baz",
|
||||
Type: mount.TypeVolume,
|
||||
ReadOnly: false,
|
||||
VolumeOptions: &mount.VolumeOptions{
|
||||
Subpath: "sub",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
g, err := ServiceSpecToGRPC(s)
|
||||
assert.NilError(t, err)
|
||||
|
||||
v, ok := g.Task.Runtime.(*swarmapi.TaskSpec_Container)
|
||||
assert.Assert(t, ok)
|
||||
|
||||
assert.Check(t, is.Len(v.Container.Mounts, 1))
|
||||
assert.Check(t, is.Equal(v.Container.Mounts[0].VolumeOptions.Subpath, "sub"))
|
||||
}
|
||||
|
||||
@@ -17,12 +17,14 @@ import (
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
containerpkg "github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/daemon/cluster/convert"
|
||||
executorpkg "github.com/docker/docker/daemon/cluster/executor"
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/runconfig"
|
||||
volumeopts "github.com/docker/docker/volume/service/opts"
|
||||
gogotypes "github.com/gogo/protobuf/types"
|
||||
"github.com/moby/swarmkit/v2/agent/exec"
|
||||
@@ -290,14 +292,34 @@ func (c *containerAdapter) waitForDetach(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (c *containerAdapter) create(ctx context.Context) error {
|
||||
hostConfig := c.container.hostConfig(c.dependencies.Volumes())
|
||||
netConfig := c.container.createNetworkingConfig(c.backend)
|
||||
|
||||
// We need to make sure no empty string or "default" NetworkMode is
|
||||
// provided to the daemon as it doesn't support them.
|
||||
//
|
||||
// This is in line with what the ContainerCreate API endpoint does, but
|
||||
// unlike that endpoint we can't do that in the ServiceCreate endpoint as
|
||||
// the cluster leader and the current node might not be running on the same
|
||||
// OS. Since the normalized value isn't the same on Windows and Linux, we
|
||||
// need to make this normalization happen once we're sure we won't make a
|
||||
// cross-OS API call.
|
||||
if hostConfig.NetworkMode == "" || hostConfig.NetworkMode.IsDefault() {
|
||||
hostConfig.NetworkMode = runconfig.DefaultDaemonNetworkMode()
|
||||
if v, ok := netConfig.EndpointsConfig[network.NetworkDefault]; ok {
|
||||
delete(netConfig.EndpointsConfig, network.NetworkDefault)
|
||||
netConfig.EndpointsConfig[runconfig.DefaultDaemonNetworkMode().NetworkName()] = v
|
||||
}
|
||||
}
|
||||
|
||||
var cr containertypes.CreateResponse
|
||||
var err error
|
||||
if cr, err = c.backend.CreateManagedContainer(ctx, backend.ContainerCreateConfig{
|
||||
Name: c.container.name(),
|
||||
Config: c.container.config(),
|
||||
HostConfig: c.container.hostConfig(c.dependencies.Volumes()),
|
||||
HostConfig: hostConfig,
|
||||
// Use the first network in container create
|
||||
NetworkingConfig: c.container.createNetworkingConfig(c.backend),
|
||||
NetworkingConfig: netConfig,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -338,7 +338,8 @@ func convertMount(m api.Mount) enginemount.Mount {
|
||||
|
||||
if m.VolumeOptions != nil {
|
||||
mount.VolumeOptions = &enginemount.VolumeOptions{
|
||||
NoCopy: m.VolumeOptions.NoCopy,
|
||||
NoCopy: m.VolumeOptions.NoCopy,
|
||||
Subpath: m.VolumeOptions.Subpath,
|
||||
}
|
||||
if m.VolumeOptions.Labels != nil {
|
||||
mount.VolumeOptions.Labels = make(map[string]string, len(m.VolumeOptions.Labels))
|
||||
|
||||
@@ -52,7 +52,7 @@ func NewExecutor(b executorpkg.Backend, p plugin.Backend, i executorpkg.ImageBac
|
||||
pluginBackend: p,
|
||||
imageBackend: i,
|
||||
volumeBackend: v,
|
||||
dependencies: agent.NewDependencyManager(b.PluginGetter()),
|
||||
dependencies: agent.NewDependencyManager(convert.SwarmPluginGetter(b.PluginGetter())),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,12 @@ import (
|
||||
|
||||
"github.com/containerd/log"
|
||||
types "github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/daemon/cluster/convert"
|
||||
"github.com/docker/docker/daemon/cluster/executor/container"
|
||||
lncluster "github.com/docker/docker/libnetwork/cluster"
|
||||
"github.com/docker/docker/libnetwork/cnmallocator"
|
||||
swarmapi "github.com/moby/swarmkit/v2/api"
|
||||
swarmallocator "github.com/moby/swarmkit/v2/manager/allocator/cnmallocator"
|
||||
"github.com/moby/swarmkit/v2/manager/allocator/networkallocator"
|
||||
swarmnode "github.com/moby/swarmkit/v2/node"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/grpc"
|
||||
@@ -123,7 +125,7 @@ func (n *nodeRunner) start(conf nodeStartConfig) error {
|
||||
ListenControlAPI: control,
|
||||
ListenRemoteAPI: conf.ListenAddr,
|
||||
AdvertiseRemoteAPI: conf.AdvertiseAddr,
|
||||
NetworkConfig: &swarmallocator.NetworkConfig{
|
||||
NetworkConfig: &networkallocator.Config{
|
||||
DefaultAddrPool: conf.DefaultAddressPool,
|
||||
SubnetSize: conf.SubnetSize,
|
||||
VXLANUDPPort: conf.DataPathPort,
|
||||
@@ -144,7 +146,8 @@ func (n *nodeRunner) start(conf nodeStartConfig) error {
|
||||
ElectionTick: n.cluster.config.RaftElectionTick,
|
||||
UnlockKey: conf.lockKey,
|
||||
AutoLockManagers: conf.autolock,
|
||||
PluginGetter: n.cluster.config.Backend.PluginGetter(),
|
||||
PluginGetter: convert.SwarmPluginGetter(n.cluster.config.Backend.PluginGetter()),
|
||||
NetworkProvider: cnmallocator.NewProvider(n.cluster.config.Backend.PluginGetter()),
|
||||
}
|
||||
if conf.availability != "" {
|
||||
avail, ok := swarmapi.NodeSpec_Availability_value[strings.ToUpper(string(conf.availability))]
|
||||
|
||||
@@ -196,6 +196,10 @@ func (conf *Config) ValidatePlatformConfig() error {
|
||||
return errors.Wrap(err, "invalid fixed-cidr-v6")
|
||||
}
|
||||
|
||||
if _, ok := conf.Features["windows-dns-proxy"]; ok {
|
||||
return errors.New("feature option 'windows-dns-proxy' is only available on Windows")
|
||||
}
|
||||
|
||||
return verifyDefaultCgroupNsMode(conf.CgroupNamespaceMode)
|
||||
}
|
||||
|
||||
|
||||
@@ -364,6 +364,6 @@ func translateWorkingDir(config *containertypes.Config) error {
|
||||
if !system.IsAbs(wd) {
|
||||
return fmt.Errorf("the working directory '%s' is invalid, it needs to be an absolute path", config.WorkingDir)
|
||||
}
|
||||
config.WorkingDir = wd
|
||||
config.WorkingDir = filepath.Clean(wd)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -54,7 +54,8 @@ func (daemon *Daemon) buildSandboxOptions(cfg *config.Config, container *contain
|
||||
sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey())
|
||||
}
|
||||
|
||||
if err := setupPathsAndSandboxOptions(container, cfg, &sboxOptions); err != nil {
|
||||
// Add platform-specific Sandbox options.
|
||||
if err := buildSandboxPlatformOptions(container, cfg, &sboxOptions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -420,9 +421,6 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai
|
||||
}
|
||||
|
||||
networkName := mode.NetworkName()
|
||||
if mode.IsDefault() {
|
||||
networkName = daemon.netController.Config().DefaultNetwork
|
||||
}
|
||||
|
||||
if mode.IsUserDefined() {
|
||||
var err error
|
||||
@@ -461,15 +459,6 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai
|
||||
}
|
||||
}
|
||||
|
||||
// Convert any settings added by client in default name to
|
||||
// engine's default network name key
|
||||
if mode.IsDefault() {
|
||||
if nConf, ok := container.NetworkSettings.Networks[mode.NetworkName()]; ok {
|
||||
container.NetworkSettings.Networks[networkName] = nConf
|
||||
delete(container.NetworkSettings.Networks, mode.NetworkName())
|
||||
}
|
||||
}
|
||||
|
||||
if !mode.IsUserDefined() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -417,7 +417,7 @@ func serviceDiscoveryOnDefaultNetwork() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func setupPathsAndSandboxOptions(container *container.Container, cfg *config.Config, sboxOptions *[]libnetwork.SandboxOption) error {
|
||||
func buildSandboxPlatformOptions(container *container.Container, cfg *config.Config, sboxOptions *[]libnetwork.SandboxOption) error {
|
||||
var err error
|
||||
var originResolvConfPath string
|
||||
|
||||
@@ -481,6 +481,7 @@ func setupPathsAndSandboxOptions(container *container.Container, cfg *config.Con
|
||||
return err
|
||||
}
|
||||
*sboxOptions = append(*sboxOptions, libnetwork.OptionResolvConfPath(container.ResolvConfPath))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -163,7 +163,13 @@ func serviceDiscoveryOnDefaultNetwork() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func setupPathsAndSandboxOptions(container *container.Container, cfg *config.Config, sboxOptions *[]libnetwork.SandboxOption) error {
|
||||
func buildSandboxPlatformOptions(container *container.Container, cfg *config.Config, sboxOptions *[]libnetwork.SandboxOption) error {
|
||||
// By default, the Windows internal resolver does not forward requests to
|
||||
// external resolvers - but forwarding can be enabled using feature flag
|
||||
// "windows-dns-proxy":true.
|
||||
if doproxy, exists := cfg.Features["windows-dns-proxy"]; !exists || !doproxy {
|
||||
*sboxOptions = append(*sboxOptions, libnetwork.OptionDNSNoProxy())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -301,6 +301,13 @@ func (i *ImageService) imageSummary(ctx context.Context, img images.Image, platf
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, errNotManifestOrIndex) {
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"error": err,
|
||||
"image": img.Name,
|
||||
}).Warn("unexpected image target (neither a manifest nor index)")
|
||||
return nil, nil, nil
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -101,6 +101,9 @@ func TestImageList(t *testing.T) {
|
||||
emptyIndex, err := specialimage.EmptyIndex(blobsDir)
|
||||
assert.NilError(t, err)
|
||||
|
||||
configTarget, err := specialimage.ConfigTarget(blobsDir)
|
||||
assert.NilError(t, err)
|
||||
|
||||
cs := &blobsDirContentStore{blobs: filepath.Join(blobsDir, "blobs/sha256")}
|
||||
|
||||
for _, tc := range []struct {
|
||||
@@ -150,6 +153,16 @@ func TestImageList(t *testing.T) {
|
||||
assert.Check(t, is.Len(all, 2))
|
||||
},
|
||||
},
|
||||
{
|
||||
// Make sure an invalid image target doesn't break the whole operation
|
||||
name: "one good image, second has config as a target",
|
||||
images: imagesFromIndex(multilayer, configTarget),
|
||||
check: func(t *testing.T, all []*imagetypes.Summary) {
|
||||
assert.Check(t, is.Len(all, 1))
|
||||
|
||||
assert.Check(t, is.Equal(all[0].ID, multilayer.Manifests[0].Digest.String()))
|
||||
},
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
@@ -45,7 +45,7 @@ func (i *ImageService) walkImageManifests(ctx context.Context, img containerdima
|
||||
return i.walkPresentChildren(ctx, desc, handleManifest)
|
||||
}
|
||||
|
||||
return errNotManifestOrIndex
|
||||
return errors.Wrapf(errNotManifestOrIndex, "error walking manifest for %s", img.Name)
|
||||
}
|
||||
|
||||
type ImageManifest struct {
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
imagetypes "github.com/docker/docker/api/types/image"
|
||||
networktypes "github.com/docker/docker/api/types/network"
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
@@ -373,6 +374,21 @@ func (daemon *Daemon) restore(cfg *configStore) error {
|
||||
Type: local.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize the "default" network mode into the network mode
|
||||
// it aliases ("bridge on Linux and "nat" on Windows). This is
|
||||
// also done by the container router, for new containers. But
|
||||
// we need to do it here too to handle containers that were
|
||||
// created prior to v26.0.
|
||||
//
|
||||
// TODO(aker): remove this migration code once the next LTM version of MCR is released.
|
||||
if c.HostConfig.NetworkMode.IsDefault() {
|
||||
c.HostConfig.NetworkMode = runconfig.DefaultDaemonNetworkMode()
|
||||
if nw, ok := c.NetworkSettings.Networks[networktypes.NetworkDefault]; ok {
|
||||
c.NetworkSettings.Networks[c.HostConfig.NetworkMode.NetworkName()] = nw
|
||||
delete(c.NetworkSettings.Networks, networktypes.NetworkDefault)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := daemon.checkpointAndSave(c); err != nil {
|
||||
|
||||
@@ -104,8 +104,8 @@ func getMemoryResources(config containertypes.Resources) *specs.LinuxMemory {
|
||||
memory.DisableOOMKiller = config.OomKillDisable
|
||||
}
|
||||
|
||||
if config.KernelMemory != 0 {
|
||||
memory.Kernel = &config.KernelMemory
|
||||
if config.KernelMemory != 0 { //nolint:staticcheck // ignore SA1019: memory.Kernel is deprecated: kernel-memory limits are not supported in cgroups v2, and were obsoleted in [kernel v5.4]. This field should no longer be used, as it may be ignored by runtimes.
|
||||
memory.Kernel = &config.KernelMemory //nolint:staticcheck // ignore SA1019: memory.Kernel is deprecated: kernel-memory limits are not supported in cgroups v2, and were obsoleted in [kernel v5.4]. This field should no longer be used, as it may be ignored by runtimes.
|
||||
}
|
||||
|
||||
if config.KernelMemoryTCP != 0 {
|
||||
|
||||
@@ -130,12 +130,8 @@ func (daemon *Daemon) getInspectData(daemonCfg *config.Config, container *contai
|
||||
// unconditionally, to keep backward compatibility with clients that use
|
||||
// unversioned API endpoints.
|
||||
if container.Config != nil && container.Config.MacAddress == "" { //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
|
||||
if nwm := hostConfig.NetworkMode; nwm.IsDefault() || nwm.IsBridge() || nwm.IsUserDefined() {
|
||||
name := nwm.NetworkName()
|
||||
if nwm.IsDefault() {
|
||||
name = daemon.netController.Config().DefaultNetwork
|
||||
}
|
||||
if epConf, ok := container.NetworkSettings.Networks[name]; ok {
|
||||
if nwm := hostConfig.NetworkMode; nwm.IsBridge() || nwm.IsUserDefined() {
|
||||
if epConf, ok := container.NetworkSettings.Networks[nwm.NetworkName()]; ok {
|
||||
container.Config.MacAddress = epConf.DesiredMacAddress //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,13 @@ func setNvidiaGPUs(s *specs.Spec, dev *deviceInstance) error {
|
||||
if s.Hooks == nil {
|
||||
s.Hooks = &specs.Hooks{}
|
||||
}
|
||||
s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{
|
||||
|
||||
// This implementation uses prestart hooks, which are deprecated.
|
||||
// CreateRuntime is the closest equivalent, and executed in the same
|
||||
// locations as prestart-hooks, but depending on what these hooks do,
|
||||
// possibly one of the other hooks could be used instead (such as
|
||||
// CreateContainer or StartContainer).
|
||||
s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{ //nolint:staticcheck // FIXME(thaJeztah); replace prestart hook with a non-deprecated one.
|
||||
Path: path,
|
||||
Args: []string{
|
||||
nvidiaHook,
|
||||
|
||||
@@ -73,7 +73,7 @@ func withLibnetwork(daemon *Daemon, daemonCfg *dconfig.Config, c *container.Cont
|
||||
s.Hooks = &specs.Hooks{}
|
||||
}
|
||||
shortNetCtlrID := stringid.TruncateID(daemon.netController.ID())
|
||||
s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{
|
||||
s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{ //nolint:staticcheck // FIXME(thaJeztah); replace prestart hook with a non-deprecated one.
|
||||
Path: filepath.Join("/proc", strconv.Itoa(os.Getpid()), "exe"),
|
||||
Args: []string{"libnetwork-setkey", "-exec-root=" + daemonCfg.GetExecRoot(), c.ID, shortNetCtlrID},
|
||||
})
|
||||
|
||||
@@ -59,8 +59,8 @@ func toContainerdResources(resources container.Resources) *libcontainerdtypes.Re
|
||||
if resources.MemoryReservation != 0 {
|
||||
memory.Reservation = &resources.MemoryReservation
|
||||
}
|
||||
if resources.KernelMemory != 0 {
|
||||
memory.Kernel = &resources.KernelMemory
|
||||
if resources.KernelMemory != 0 { //nolint:staticcheck // ignore SA1019: memory.Kernel is deprecated: kernel-memory limits are not supported in cgroups v2, and were obsoleted in [kernel v5.4]. This field should no longer be used, as it may be ignored by runtimes.
|
||||
memory.Kernel = &resources.KernelMemory //nolint:staticcheck // ignore SA1019: memory.Kernel is deprecated: kernel-memory limits are not supported in cgroups v2, and were obsoleted in [kernel v5.4]. This field should no longer be used, as it may be ignored by runtimes.
|
||||
}
|
||||
if resources.MemorySwap > 0 {
|
||||
memory.Swap = &resources.MemorySwap
|
||||
|
||||
@@ -4296,7 +4296,7 @@ func (s *DockerCLIBuildSuite) TestBuildBuildTimeArgExpansion(c *testing.T) {
|
||||
imgName := "bldvarstest"
|
||||
|
||||
wdVar := "WDIR"
|
||||
wdVal := "/tmp/"
|
||||
wdVal := "/tmp"
|
||||
addVar := "AFILE"
|
||||
addVal := "addFile"
|
||||
copyVar := "CFILE"
|
||||
|
||||
@@ -689,6 +689,72 @@ func TestBuildPlatformInvalid(t *testing.T) {
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||
}
|
||||
|
||||
// TestBuildWorkdirNoCacheMiss is a regression test for https://github.com/moby/moby/issues/47627
|
||||
func TestBuildWorkdirNoCacheMiss(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
dockerfile string
|
||||
}{
|
||||
{name: "trailing slash", dockerfile: "FROM busybox\nWORKDIR /foo/"},
|
||||
{name: "no trailing slash", dockerfile: "FROM busybox\nWORKDIR /foo"},
|
||||
} {
|
||||
dockerfile := tc.dockerfile
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile))
|
||||
defer source.Close()
|
||||
|
||||
apiClient := testEnv.APIClient()
|
||||
|
||||
buildAndGetID := func() string {
|
||||
resp, err := apiClient.ImageBuild(ctx, source.AsTarReader(t), types.ImageBuildOptions{
|
||||
Version: types.BuilderV1,
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
id := readBuildImageIDs(t, resp.Body)
|
||||
assert.Check(t, id != "")
|
||||
return id
|
||||
}
|
||||
|
||||
firstId := buildAndGetID()
|
||||
secondId := buildAndGetID()
|
||||
|
||||
assert.Check(t, is.Equal(firstId, secondId), "expected cache to be used")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func readBuildImageIDs(t *testing.T, rd io.Reader) string {
|
||||
t.Helper()
|
||||
decoder := json.NewDecoder(rd)
|
||||
for {
|
||||
var jm jsonmessage.JSONMessage
|
||||
if err := decoder.Decode(&jm); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
if jm.Aux == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var auxId struct {
|
||||
ID string `json:"ID"`
|
||||
}
|
||||
|
||||
if json.Unmarshal(*jm.Aux, &auxId); auxId.ID != "" {
|
||||
return auxId.ID
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func writeTarRecord(t *testing.T, w *tar.Writer, fn, contents string) {
|
||||
err := w.WriteHeader(&tar.Header{
|
||||
Name: fn,
|
||||
|
||||
@@ -326,3 +326,33 @@ func TestStaticIPOutsideSubpool(t *testing.T) {
|
||||
|
||||
assert.Check(t, is.Contains(b.String(), "inet 10.42.1.3/16"))
|
||||
}
|
||||
|
||||
func TestWorkingDirNormalization(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
apiClient := testEnv.APIClient()
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
workdir string
|
||||
}{
|
||||
{name: "trailing slash", workdir: "/tmp/"},
|
||||
{name: "no trailing slash", workdir: "/tmp"},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cID := container.Run(ctx, t, apiClient,
|
||||
container.WithImage("busybox"),
|
||||
container.WithWorkingDir(tc.workdir),
|
||||
)
|
||||
|
||||
defer container.Remove(ctx, t, apiClient, cID, containertypes.RemoveOptions{Force: true})
|
||||
|
||||
inspect := container.Inspect(ctx, t, apiClient, cID)
|
||||
|
||||
assert.Check(t, is.Equal(inspect.Config.WorkingDir, "/tmp"))
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,13 @@ func WithNetworkMode(mode string) func(*TestContainerConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
// WithDNS sets external DNS servers for the container
|
||||
func WithDNS(dns []string) func(*TestContainerConfig) {
|
||||
return func(c *TestContainerConfig) {
|
||||
c.HostConfig.DNS = append([]string(nil), dns...)
|
||||
}
|
||||
}
|
||||
|
||||
// WithSysctls sets sysctl options for the container
|
||||
func WithSysctls(sysctls map[string]string) func(*TestContainerConfig) {
|
||||
return func(c *TestContainerConfig) {
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"github.com/docker/docker/integration/internal/network"
|
||||
"github.com/docker/docker/testutil"
|
||||
"github.com/docker/docker/testutil/daemon"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/poll"
|
||||
"gotest.tools/v3/skip"
|
||||
)
|
||||
@@ -33,3 +35,59 @@ func TestDaemonDNSFallback(t *testing.T) {
|
||||
|
||||
poll.WaitOn(t, container.IsSuccessful(ctx, c, cid), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(10*time.Second))
|
||||
}
|
||||
|
||||
// Check that, when the internal DNS server's address is supplied as an external
|
||||
// DNS server, the daemon doesn't start talking to itself.
|
||||
func TestIntDNSAsExtDNS(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "cannot start daemon on Windows test run")
|
||||
skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run")
|
||||
|
||||
ctx := setupTest(t)
|
||||
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(ctx, t)
|
||||
defer d.Stop(t)
|
||||
|
||||
c := d.NewClientT(t)
|
||||
defer c.Close()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
extServers []string
|
||||
expExitCode int
|
||||
expStdout string
|
||||
}{
|
||||
{
|
||||
name: "only self",
|
||||
extServers: []string{"127.0.0.11"},
|
||||
expExitCode: 1,
|
||||
expStdout: "SERVFAIL",
|
||||
},
|
||||
{
|
||||
name: "self then ext",
|
||||
extServers: []string{"127.0.0.11", "8.8.8.8"},
|
||||
expExitCode: 0,
|
||||
expStdout: "Non-authoritative answer",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := testutil.StartSpan(ctx, t)
|
||||
|
||||
const netName = "testnet"
|
||||
network.CreateNoError(ctx, t, c, netName)
|
||||
defer network.RemoveNoError(ctx, t, c, netName)
|
||||
|
||||
res := container.RunAttach(ctx, t, c,
|
||||
container.WithNetworkMode(netName),
|
||||
container.WithDNS(tc.extServers),
|
||||
container.WithCmd("nslookup", "docker.com"),
|
||||
)
|
||||
defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{Force: true})
|
||||
|
||||
assert.Check(t, is.Equal(res.ExitCode, tc.expExitCode))
|
||||
assert.Check(t, is.Contains(res.Stdout.String(), tc.expStdout))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,7 +469,6 @@ func TestDefaultBridgeAddresses(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
|
||||
|
||||
ctx := setupTest(t)
|
||||
d := daemon.New(t)
|
||||
|
||||
type testStep struct {
|
||||
stepName string
|
||||
@@ -487,13 +486,13 @@ func TestDefaultBridgeAddresses(t *testing.T) {
|
||||
{
|
||||
stepName: "Set up initial UL prefix",
|
||||
fixedCIDRV6: "fd1c:f1a0:5d8d:aaaa::/64",
|
||||
expAddrs: []string{"fd1c:f1a0:5d8d:aaaa::1/64", "fe80::1/64"},
|
||||
expAddrs: []string{"fd1c:f1a0:5d8d:aaaa::1/64", "fe80::"},
|
||||
},
|
||||
{
|
||||
// Modify that prefix, the default bridge's address must be deleted and re-added.
|
||||
stepName: "Modify UL prefix - address change",
|
||||
fixedCIDRV6: "fd1c:f1a0:5d8d:bbbb::/64",
|
||||
expAddrs: []string{"fd1c:f1a0:5d8d:bbbb::1/64", "fe80::1/64"},
|
||||
expAddrs: []string{"fd1c:f1a0:5d8d:bbbb::1/64", "fe80::"},
|
||||
},
|
||||
{
|
||||
// Modify the prefix length, the default bridge's address should not change.
|
||||
@@ -501,7 +500,7 @@ func TestDefaultBridgeAddresses(t *testing.T) {
|
||||
fixedCIDRV6: "fd1c:f1a0:5d8d:bbbb::/80",
|
||||
// The prefix length displayed by 'ip a' is not updated - it's informational, and
|
||||
// can't be changed without unnecessarily deleting and re-adding the address.
|
||||
expAddrs: []string{"fd1c:f1a0:5d8d:bbbb::1/64", "fe80::1/64"},
|
||||
expAddrs: []string{"fd1c:f1a0:5d8d:bbbb::1/64", "fe80::"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -511,14 +510,14 @@ func TestDefaultBridgeAddresses(t *testing.T) {
|
||||
{
|
||||
stepName: "Standard LL subnet prefix",
|
||||
fixedCIDRV6: "fe80::/64",
|
||||
expAddrs: []string{"fe80::1/64"},
|
||||
expAddrs: []string{"fe80::"},
|
||||
},
|
||||
{
|
||||
// Modify that prefix, the default bridge's address must be deleted and re-added.
|
||||
// The bridge must still have an address in the required (standard) LL subnet.
|
||||
stepName: "Nonstandard LL prefix - address change",
|
||||
fixedCIDRV6: "fe80:1234::/32",
|
||||
expAddrs: []string{"fe80:1234::1/32", "fe80::1/64"},
|
||||
expAddrs: []string{"fe80:1234::1/32", "fe80::"},
|
||||
},
|
||||
{
|
||||
// Modify the prefix length, the addresses should not change.
|
||||
@@ -526,32 +525,48 @@ func TestDefaultBridgeAddresses(t *testing.T) {
|
||||
fixedCIDRV6: "fe80:1234::/64",
|
||||
// The prefix length displayed by 'ip a' is not updated - it's informational, and
|
||||
// can't be changed without unnecessarily deleting and re-adding the address.
|
||||
expAddrs: []string{"fe80:1234::1/", "fe80::1/64"},
|
||||
expAddrs: []string{"fe80:1234::1/", "fe80::"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, step := range tc.steps {
|
||||
// Check that the daemon starts - regression test for:
|
||||
// https://github.com/moby/moby/issues/46829
|
||||
d.Start(t, "--experimental", "--ipv6", "--ip6tables", "--fixed-cidr-v6="+step.fixedCIDRV6)
|
||||
d.Stop(t)
|
||||
for _, preserveKernelLL := range []bool{false, true} {
|
||||
var dopts []daemon.Option
|
||||
if preserveKernelLL {
|
||||
dopts = append(dopts, daemon.WithEnvVars("DOCKER_BRIDGE_PRESERVE_KERNEL_LL=1"))
|
||||
}
|
||||
d := daemon.New(t, dopts...)
|
||||
c := d.NewClientT(t)
|
||||
|
||||
// Check that the expected addresses have been applied to the bridge. (Skip in
|
||||
// rootless mode, because the bridge is in a different network namespace.)
|
||||
if !testEnv.IsRootless() {
|
||||
res := testutil.RunCommand(ctx, "ip", "-6", "addr", "show", "docker0")
|
||||
assert.Equal(t, res.ExitCode, 0, step.stepName)
|
||||
stdout := res.Stdout()
|
||||
for _, expAddr := range step.expAddrs {
|
||||
assert.Check(t, is.Contains(stdout, expAddr))
|
||||
for _, tc := range testcases {
|
||||
for _, step := range tc.steps {
|
||||
tcName := fmt.Sprintf("kernel_ll_%v/%s/%s", preserveKernelLL, tc.name, step.stepName)
|
||||
t.Run(tcName, func(t *testing.T) {
|
||||
ctx := testutil.StartSpan(ctx, t)
|
||||
// Check that the daemon starts - regression test for:
|
||||
// https://github.com/moby/moby/issues/46829
|
||||
d.StartWithBusybox(ctx, t, "--experimental", "--ipv6", "--ip6tables", "--fixed-cidr-v6="+step.fixedCIDRV6)
|
||||
|
||||
// Start a container, so that the bridge is set "up" and gets a kernel_ll address.
|
||||
cID := container.Run(ctx, t, c)
|
||||
defer c.ContainerRemove(ctx, cID, containertypes.RemoveOptions{Force: true})
|
||||
|
||||
d.Stop(t)
|
||||
|
||||
// Check that the expected addresses have been applied to the bridge. (Skip in
|
||||
// rootless mode, because the bridge is in a different network namespace.)
|
||||
if !testEnv.IsRootless() {
|
||||
res := testutil.RunCommand(ctx, "ip", "-6", "addr", "show", "docker0")
|
||||
assert.Equal(t, res.ExitCode, 0, step.stepName)
|
||||
stdout := res.Stdout()
|
||||
for _, expAddr := range step.expAddrs {
|
||||
assert.Check(t, is.Contains(stdout, expAddr))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -738,3 +753,78 @@ func TestSetInterfaceSysctl(t *testing.T) {
|
||||
stdout := runRes.Stdout.String()
|
||||
assert.Check(t, is.Contains(stdout, scName))
|
||||
}
|
||||
|
||||
// With a read-only "/proc/sys/net" filesystem (simulated using env var
|
||||
// DOCKER_TEST_RO_DISABLE_IPV6), check that if IPv6 can't be disabled on a
|
||||
// container interface, container creation fails - unless the error is ignored by
|
||||
// setting env var DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1.
|
||||
// Regression test for https://github.com/moby/moby/issues/47751
|
||||
func TestReadOnlySlashProc(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
|
||||
|
||||
ctx := setupTest(t)
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
daemonEnv []string
|
||||
expErr string
|
||||
}{
|
||||
{
|
||||
name: "Normality",
|
||||
},
|
||||
{
|
||||
name: "Read only no workaround",
|
||||
daemonEnv: []string{
|
||||
"DOCKER_TEST_RO_DISABLE_IPV6=1",
|
||||
},
|
||||
expErr: "failed to disable IPv6 on container's interface eth0, set env var DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1 to ignore this error",
|
||||
},
|
||||
{
|
||||
name: "Read only with workaround",
|
||||
daemonEnv: []string{
|
||||
"DOCKER_TEST_RO_DISABLE_IPV6=1",
|
||||
"DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := testutil.StartSpan(ctx, t)
|
||||
|
||||
d := daemon.New(t, daemon.WithEnvVars(tc.daemonEnv...))
|
||||
d.StartWithBusybox(ctx, t)
|
||||
defer d.Stop(t)
|
||||
c := d.NewClientT(t)
|
||||
|
||||
const net4Name = "testnet4"
|
||||
network.CreateNoError(ctx, t, c, net4Name)
|
||||
defer network.RemoveNoError(ctx, t, c, net4Name)
|
||||
id4 := container.Create(ctx, t, c,
|
||||
container.WithNetworkMode(net4Name),
|
||||
container.WithCmd("ls"),
|
||||
)
|
||||
defer c.ContainerRemove(ctx, id4, containertypes.RemoveOptions{Force: true})
|
||||
err := c.ContainerStart(ctx, id4, containertypes.StartOptions{})
|
||||
if tc.expErr == "" {
|
||||
assert.Check(t, err)
|
||||
} else {
|
||||
assert.Check(t, is.ErrorContains(err, tc.expErr))
|
||||
}
|
||||
|
||||
// It should always be possible to create a container on an IPv6 network (IPv6
|
||||
// doesn't need to be disabled on the interface).
|
||||
const net6Name = "testnet6"
|
||||
network.CreateNoError(ctx, t, c, net6Name,
|
||||
network.WithIPv6(),
|
||||
network.WithIPAM("fd5c:15e3:0b62:5395::/64", "fd5c:15e3:0b62:5395::1"),
|
||||
)
|
||||
defer network.RemoveNoError(ctx, t, c, net6Name)
|
||||
id6 := container.Run(ctx, t, c,
|
||||
container.WithNetworkMode(net6Name),
|
||||
container.WithCmd("ls"),
|
||||
)
|
||||
defer c.ContainerRemove(ctx, id6, containertypes.RemoveOptions{Force: true})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package networking
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
"github.com/docker/docker/integration/internal/network"
|
||||
"github.com/docker/docker/testutil/daemon"
|
||||
@@ -131,3 +136,80 @@ func TestInternalNetworkDNS(t *testing.T) {
|
||||
assert.Check(t, is.Equal(res.ExitCode, 0))
|
||||
assert.Check(t, is.Contains(res.Stdout(), network.DNSRespAddr))
|
||||
}
|
||||
|
||||
// Check that '--dns' can be used to name a server inside a '--internal' network.
|
||||
// Regression test for https://github.com/moby/moby/issues/47822
|
||||
func TestInternalNetworkLocalDNS(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "No internal networks on Windows")
|
||||
skip.If(t, testEnv.IsRootless, "Can't write an accessible dnsd.conf in rootless mode")
|
||||
ctx := setupTest(t)
|
||||
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(ctx, t)
|
||||
defer d.Stop(t)
|
||||
|
||||
c := d.NewClientT(t)
|
||||
defer c.Close()
|
||||
|
||||
intNetName := "intnet"
|
||||
network.CreateNoError(ctx, t, c, intNetName,
|
||||
network.WithDriver("bridge"),
|
||||
network.WithInternal(),
|
||||
)
|
||||
defer network.RemoveNoError(ctx, t, c, intNetName)
|
||||
|
||||
// Write a config file for busybox's dnsd.
|
||||
td := t.TempDir()
|
||||
fname := path.Join(td, "dnsd.conf")
|
||||
err := os.WriteFile(fname, []byte("foo.example 192.0.2.42\n"), 0644)
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Start a DNS server on the internal network.
|
||||
serverId := container.Run(ctx, t, c,
|
||||
container.WithNetworkMode(intNetName),
|
||||
container.WithMount(mount.Mount{
|
||||
Type: mount.TypeBind,
|
||||
Source: fname,
|
||||
Target: "/etc/dnsd.conf",
|
||||
}),
|
||||
container.WithCmd("dnsd"),
|
||||
)
|
||||
defer c.ContainerRemove(ctx, serverId, containertypes.RemoveOptions{Force: true})
|
||||
|
||||
// Get the DNS server's address.
|
||||
inspect := container.Inspect(ctx, t, c, serverId)
|
||||
serverIP := inspect.NetworkSettings.Networks[intNetName].IPAddress
|
||||
|
||||
// Query the internal network's DNS server (via the daemon's internal DNS server).
|
||||
res := container.RunAttach(ctx, t, c,
|
||||
container.WithNetworkMode(intNetName),
|
||||
container.WithDNS([]string{serverIP}),
|
||||
container.WithCmd("nslookup", "-type=A", "foo.example"),
|
||||
)
|
||||
defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{Force: true})
|
||||
assert.Check(t, is.Contains(res.Stdout.String(), "192.0.2.42"))
|
||||
}
|
||||
|
||||
// TestNslookupWindows checks that nslookup gets results from external DNS.
|
||||
// Regression test for https://github.com/moby/moby/issues/46792
|
||||
func TestNslookupWindows(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType != "windows")
|
||||
|
||||
ctx := setupTest(t)
|
||||
c := testEnv.APIClient()
|
||||
|
||||
attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
res := container.RunAttach(attachCtx, t, c,
|
||||
container.WithCmd("nslookup", "docker.com"),
|
||||
)
|
||||
defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{Force: true})
|
||||
|
||||
assert.Check(t, is.Equal(res.ExitCode, 0))
|
||||
// Current default is to not-forward requests to external servers, which
|
||||
// can only be changed in daemon.json using feature flag "windows-dns-proxy".
|
||||
// So, expect the lookup to fail...
|
||||
assert.Check(t, is.Contains(res.Stderr.String(), "Server failed"))
|
||||
// When the default behaviour is changed, nslookup should succeed...
|
||||
//assert.Check(t, is.Contains(res.Stdout.String(), "Addresses:"))
|
||||
}
|
||||
|
||||
33
internal/testutils/specialimage/configtarget.go
Normal file
33
internal/testutils/specialimage/configtarget.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package specialimage
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/distribution/reference"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// ConfigTarget creates an image index with an image config being used as an
|
||||
// image target instead of a manifest or index.
|
||||
func ConfigTarget(dir string) (*ocispec.Index, error) {
|
||||
const imageRef = "config:latest"
|
||||
|
||||
ref, err := reference.ParseNormalizedNamed(imageRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
desc, err := writeJsonBlob(dir, ocispec.MediaTypeImageConfig, ocispec.Image{
|
||||
Platform: platforms.MustParse("linux/amd64"),
|
||||
Config: ocispec.ImageConfig{
|
||||
Env: []string{"FOO=BAR"},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
desc.Annotations = map[string]string{
|
||||
"io.containerd.image.name": ref.String(),
|
||||
}
|
||||
|
||||
return ociImage(dir, ref, desc)
|
||||
}
|
||||
@@ -102,19 +102,24 @@ func singlePlatformImage(dir string, ref reference.Named, manifest ocispec.Manif
|
||||
}
|
||||
}
|
||||
|
||||
idx := ocispec.Index{
|
||||
Versioned: specs.Versioned{SchemaVersion: 2},
|
||||
MediaType: ocispec.MediaTypeImageIndex,
|
||||
Manifests: []ocispec.Descriptor{manifestDesc},
|
||||
}
|
||||
if err := writeJson(idx, filepath.Join(dir, "index.json")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := writeJson(legacyManifests, filepath.Join(dir, "manifest.json")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = os.WriteFile(filepath.Join(dir, "oci-layout"), []byte(`{"imageLayoutVersion": "1.0.0"}`), 0o644)
|
||||
return ociImage(dir, ref, manifestDesc)
|
||||
}
|
||||
|
||||
func ociImage(dir string, ref reference.Named, target ocispec.Descriptor) (*ocispec.Index, error) {
|
||||
idx := ocispec.Index{
|
||||
Versioned: specs.Versioned{SchemaVersion: 2},
|
||||
MediaType: ocispec.MediaTypeImageIndex,
|
||||
Manifests: []ocispec.Descriptor{target},
|
||||
}
|
||||
if err := writeJson(idx, filepath.Join(dir, "index.json")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := os.WriteFile(filepath.Join(dir, "oci-layout"), []byte(`{"imageLayoutVersion": "1.0.0"}`), 0o644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
14
libnetwork/cnmallocator/allocator_test.go
Normal file
14
libnetwork/cnmallocator/allocator_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package cnmallocator
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/moby/swarmkit/v2/manager/allocator"
|
||||
"gotest.tools/v3/skip"
|
||||
)
|
||||
|
||||
func TestAllocator(t *testing.T) {
|
||||
skip.If(t, runtime.GOOS == "windows", "Allocator tests are hardcoded to use Linux network driver names")
|
||||
allocator.RunAllocatorTests(t, NewProvider(nil))
|
||||
}
|
||||
@@ -11,6 +11,6 @@ var initializers = map[string]func(driverapi.Registerer) error{
|
||||
}
|
||||
|
||||
// PredefinedNetworks returns the list of predefined network structures
|
||||
func PredefinedNetworks() []networkallocator.PredefinedNetworkData {
|
||||
func (*Provider) PredefinedNetworks() []networkallocator.PredefinedNetworkData {
|
||||
return nil
|
||||
}
|
||||
@@ -5,14 +5,15 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/docker/docker/libnetwork/ipamapi"
|
||||
builtinIpam "github.com/docker/docker/libnetwork/ipams/builtin"
|
||||
nullIpam "github.com/docker/docker/libnetwork/ipams/null"
|
||||
"github.com/docker/docker/libnetwork/ipamutils"
|
||||
"github.com/moby/swarmkit/v2/log"
|
||||
"github.com/moby/swarmkit/v2/manager/allocator/networkallocator"
|
||||
)
|
||||
|
||||
func initIPAMDrivers(r ipamapi.Registerer, netConfig *NetworkConfig) error {
|
||||
func initIPAMDrivers(r ipamapi.Registerer, netConfig *networkallocator.Config) error {
|
||||
var addressPool []*ipamutils.NetworkToSplit
|
||||
var str strings.Builder
|
||||
str.WriteString("Subnetlist - ")
|
||||
@@ -19,7 +19,7 @@ var initializers = map[string]func(driverapi.Registerer) error{
|
||||
}
|
||||
|
||||
// PredefinedNetworks returns the list of predefined network structures
|
||||
func PredefinedNetworks() []networkallocator.PredefinedNetworkData {
|
||||
func (*Provider) PredefinedNetworks() []networkallocator.PredefinedNetworkData {
|
||||
return []networkallocator.PredefinedNetworkData{
|
||||
{Name: "bridge", Driver: "bridge"},
|
||||
{Name: "host", Driver: "host"},
|
||||
@@ -14,7 +14,7 @@ var initializers = map[string]func(driverapi.Registerer) error{
|
||||
}
|
||||
|
||||
// PredefinedNetworks returns the list of predefined network structures
|
||||
func PredefinedNetworks() []networkallocator.PredefinedNetworkData {
|
||||
func (*Provider) PredefinedNetworks() []networkallocator.PredefinedNetworkData {
|
||||
return []networkallocator.PredefinedNetworkData{
|
||||
{Name: "nat", Driver: "nat"},
|
||||
}
|
||||
@@ -10,6 +10,6 @@ import (
|
||||
const initializers = nil
|
||||
|
||||
// PredefinedNetworks returns the list of predefined network structures
|
||||
func PredefinedNetworks() []networkallocator.PredefinedNetworkData {
|
||||
func (*Provider) PredefinedNetworks() []networkallocator.PredefinedNetworkData {
|
||||
return nil
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/docker/docker/libnetwork/driverapi"
|
||||
"github.com/docker/docker/libnetwork/drivers/remote"
|
||||
"github.com/docker/docker/libnetwork/drvregistry"
|
||||
@@ -15,7 +16,6 @@ import (
|
||||
"github.com/docker/docker/libnetwork/scope"
|
||||
"github.com/docker/docker/pkg/plugingetter"
|
||||
"github.com/moby/swarmkit/v2/api"
|
||||
"github.com/moby/swarmkit/v2/log"
|
||||
"github.com/moby/swarmkit/v2/manager/allocator/networkallocator"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -40,9 +40,6 @@ type cnmNetworkAllocator struct {
|
||||
// The driver registry for all internal and external network drivers.
|
||||
networkRegistry drvregistry.Networks
|
||||
|
||||
// The port allocator instance for allocating node ports
|
||||
portAllocator *portAllocator
|
||||
|
||||
// Local network state used by cnmNetworkAllocator to do network management.
|
||||
networks map[string]*network
|
||||
|
||||
@@ -87,27 +84,14 @@ type networkDriver struct {
|
||||
capability *driverapi.Capability
|
||||
}
|
||||
|
||||
// NetworkConfig is used to store network related cluster config in the Manager.
|
||||
type NetworkConfig struct {
|
||||
// DefaultAddrPool specifies default subnet pool for global scope networks
|
||||
DefaultAddrPool []string
|
||||
|
||||
// SubnetSize specifies the subnet size of the networks created from
|
||||
// the default subnet pool
|
||||
SubnetSize uint32
|
||||
|
||||
// VXLANUDPPort specifies the UDP port number for VXLAN traffic
|
||||
VXLANUDPPort uint32
|
||||
}
|
||||
|
||||
// New returns a new NetworkAllocator handle
|
||||
func New(pg plugingetter.PluginGetter, netConfig *NetworkConfig) (networkallocator.NetworkAllocator, error) {
|
||||
// NewAllocator returns a new NetworkAllocator handle
|
||||
func (p *Provider) NewAllocator(netConfig *networkallocator.Config) (networkallocator.NetworkAllocator, error) {
|
||||
na := &cnmNetworkAllocator{
|
||||
networks: make(map[string]*network),
|
||||
services: make(map[string]struct{}),
|
||||
tasks: make(map[string]struct{}),
|
||||
nodes: make(map[string]map[string]struct{}),
|
||||
pg: pg,
|
||||
pg: p.pg,
|
||||
}
|
||||
|
||||
for ntype, i := range initializers {
|
||||
@@ -115,23 +99,17 @@ func New(pg plugingetter.PluginGetter, netConfig *NetworkConfig) (networkallocat
|
||||
return nil, fmt.Errorf("failed to register %q network driver: %w", ntype, err)
|
||||
}
|
||||
}
|
||||
if err := remote.Register(&na.networkRegistry, pg); err != nil {
|
||||
if err := remote.Register(&na.networkRegistry, p.pg); err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize network driver plugins: %w", err)
|
||||
}
|
||||
|
||||
if err := initIPAMDrivers(&na.ipamRegistry, netConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := remoteipam.Register(&na.ipamRegistry, pg); err != nil {
|
||||
if err := remoteipam.Register(&na.ipamRegistry, p.pg); err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize IPAM driver plugins: %w", err)
|
||||
}
|
||||
|
||||
pa, err := newPortAllocator()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
na.portAllocator = pa
|
||||
return na, nil
|
||||
}
|
||||
|
||||
@@ -209,11 +187,8 @@ func (na *cnmNetworkAllocator) Deallocate(n *api.Network) error {
|
||||
}
|
||||
|
||||
// AllocateService allocates all the network resources such as virtual
|
||||
// IP and ports needed by the service.
|
||||
// IP needed by the service.
|
||||
func (na *cnmNetworkAllocator) AllocateService(s *api.Service) (err error) {
|
||||
if err = na.portAllocator.serviceAllocatePorts(s); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
na.DeallocateService(s)
|
||||
@@ -300,7 +275,7 @@ networkLoop:
|
||||
}
|
||||
|
||||
// DeallocateService de-allocates all the network resources such as
|
||||
// virtual IP and ports associated with the service.
|
||||
// virtual IP associated with the service.
|
||||
func (na *cnmNetworkAllocator) DeallocateService(s *api.Service) error {
|
||||
if s.Endpoint == nil {
|
||||
return nil
|
||||
@@ -316,7 +291,6 @@ func (na *cnmNetworkAllocator) DeallocateService(s *api.Service) error {
|
||||
}
|
||||
s.Endpoint.VirtualIPs = nil
|
||||
|
||||
na.portAllocator.serviceDeallocatePorts(s)
|
||||
delete(na.services, s.ID)
|
||||
|
||||
return nil
|
||||
@@ -373,19 +347,8 @@ func (na *cnmNetworkAllocator) IsTaskAllocated(t *api.Task) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// HostPublishPortsNeedUpdate returns true if the passed service needs
|
||||
// allocations for its published ports in host (non ingress) mode
|
||||
func (na *cnmNetworkAllocator) HostPublishPortsNeedUpdate(s *api.Service) bool {
|
||||
return na.portAllocator.hostPublishPortsNeedUpdate(s)
|
||||
}
|
||||
|
||||
// IsServiceAllocated returns false if the passed service needs to have network resources allocated/updated.
|
||||
func (na *cnmNetworkAllocator) IsServiceAllocated(s *api.Service, flags ...func(*networkallocator.ServiceAllocationOpts)) bool {
|
||||
var options networkallocator.ServiceAllocationOpts
|
||||
for _, flag := range flags {
|
||||
flag(&options)
|
||||
}
|
||||
|
||||
specNetworks := serviceNetworks(s)
|
||||
|
||||
// If endpoint mode is VIP and allocator does not have the
|
||||
@@ -447,10 +410,6 @@ func (na *cnmNetworkAllocator) IsServiceAllocated(s *api.Service, flags ...func(
|
||||
}
|
||||
}
|
||||
|
||||
if (s.Spec.Endpoint != nil && len(s.Spec.Endpoint.Ports) != 0) ||
|
||||
(s.Endpoint != nil && len(s.Endpoint.Ports) != 0) {
|
||||
return na.portAllocator.isPortsAllocatedOnInit(s, options.OnInit)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
789
libnetwork/cnmallocator/networkallocator_test.go
Normal file
789
libnetwork/cnmallocator/networkallocator_test.go
Normal file
@@ -0,0 +1,789 @@
|
||||
package cnmallocator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/moby/swarmkit/v2/api"
|
||||
"github.com/moby/swarmkit/v2/manager/allocator/networkallocator"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func newNetworkAllocator(t *testing.T) networkallocator.NetworkAllocator {
|
||||
na, err := (&Provider{}).NewAllocator(nil)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, na != nil)
|
||||
return na
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
newNetworkAllocator(t)
|
||||
}
|
||||
|
||||
func TestAllocateInvalidIPAM(t *testing.T) {
|
||||
na := newNetworkAllocator(t)
|
||||
n := &api.Network{
|
||||
ID: "testID",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test",
|
||||
},
|
||||
DriverConfig: &api.Driver{},
|
||||
IPAM: &api.IPAMOptions{
|
||||
Driver: &api.Driver{
|
||||
Name: "invalidipam,",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
err := na.Allocate(n)
|
||||
assert.Check(t, is.ErrorContains(err, ""))
|
||||
}
|
||||
|
||||
func TestAllocateInvalidDriver(t *testing.T) {
|
||||
na := newNetworkAllocator(t)
|
||||
n := &api.Network{
|
||||
ID: "testID",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test",
|
||||
},
|
||||
DriverConfig: &api.Driver{
|
||||
Name: "invaliddriver",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := na.Allocate(n)
|
||||
assert.Check(t, is.ErrorContains(err, ""))
|
||||
}
|
||||
|
||||
func TestNetworkDoubleAllocate(t *testing.T) {
|
||||
na := newNetworkAllocator(t)
|
||||
n := &api.Network{
|
||||
ID: "testID",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := na.Allocate(n)
|
||||
assert.Check(t, err)
|
||||
|
||||
err = na.Allocate(n)
|
||||
assert.Check(t, is.ErrorContains(err, ""))
|
||||
}
|
||||
|
||||
func TestAllocateEmptyConfig(t *testing.T) {
|
||||
na1 := newNetworkAllocator(t)
|
||||
na2 := newNetworkAllocator(t)
|
||||
n1 := &api.Network{
|
||||
ID: "testID1",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
n2 := &api.Network{
|
||||
ID: "testID2",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := na1.Allocate(n1)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, n1.IPAM.Configs != nil)
|
||||
assert.Check(t, is.Equal(len(n1.IPAM.Configs), 1))
|
||||
assert.Check(t, is.Equal(n1.IPAM.Configs[0].Range, ""))
|
||||
assert.Check(t, is.Equal(len(n1.IPAM.Configs[0].Reserved), 0))
|
||||
|
||||
_, subnet11, err := net.ParseCIDR(n1.IPAM.Configs[0].Subnet)
|
||||
assert.Check(t, err)
|
||||
|
||||
gwip11 := net.ParseIP(n1.IPAM.Configs[0].Gateway)
|
||||
assert.Check(t, gwip11 != nil)
|
||||
|
||||
err = na1.Allocate(n2)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, n2.IPAM.Configs != nil)
|
||||
assert.Check(t, is.Equal(len(n2.IPAM.Configs), 1))
|
||||
assert.Check(t, is.Equal(n2.IPAM.Configs[0].Range, ""))
|
||||
assert.Check(t, is.Equal(len(n2.IPAM.Configs[0].Reserved), 0))
|
||||
|
||||
_, subnet21, err := net.ParseCIDR(n2.IPAM.Configs[0].Subnet)
|
||||
assert.Check(t, err)
|
||||
|
||||
gwip21 := net.ParseIP(n2.IPAM.Configs[0].Gateway)
|
||||
assert.Check(t, gwip21 != nil)
|
||||
|
||||
// Allocate n1 ans n2 with another allocator instance but in
|
||||
// intentionally reverse order.
|
||||
err = na2.Allocate(n2)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, n2.IPAM.Configs != nil)
|
||||
assert.Check(t, is.Equal(len(n2.IPAM.Configs), 1))
|
||||
assert.Check(t, is.Equal(n2.IPAM.Configs[0].Range, ""))
|
||||
assert.Check(t, is.Equal(len(n2.IPAM.Configs[0].Reserved), 0))
|
||||
|
||||
_, subnet22, err := net.ParseCIDR(n2.IPAM.Configs[0].Subnet)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.DeepEqual(subnet21, subnet22))
|
||||
|
||||
gwip22 := net.ParseIP(n2.IPAM.Configs[0].Gateway)
|
||||
assert.Check(t, is.DeepEqual(gwip21, gwip22))
|
||||
|
||||
err = na2.Allocate(n1)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, n1.IPAM.Configs != nil)
|
||||
assert.Check(t, is.Equal(len(n1.IPAM.Configs), 1))
|
||||
assert.Check(t, is.Equal(n1.IPAM.Configs[0].Range, ""))
|
||||
assert.Check(t, is.Equal(len(n1.IPAM.Configs[0].Reserved), 0))
|
||||
|
||||
_, subnet12, err := net.ParseCIDR(n1.IPAM.Configs[0].Subnet)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.DeepEqual(subnet11, subnet12))
|
||||
|
||||
gwip12 := net.ParseIP(n1.IPAM.Configs[0].Gateway)
|
||||
assert.Check(t, is.DeepEqual(gwip11, gwip12))
|
||||
}
|
||||
|
||||
func TestAllocateWithOneSubnet(t *testing.T) {
|
||||
na := newNetworkAllocator(t)
|
||||
n := &api.Network{
|
||||
ID: "testID",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test",
|
||||
},
|
||||
DriverConfig: &api.Driver{},
|
||||
IPAM: &api.IPAMOptions{
|
||||
Driver: &api.Driver{},
|
||||
Configs: []*api.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.1.0/24",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := na.Allocate(n)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Equal(len(n.IPAM.Configs), 1))
|
||||
assert.Check(t, is.Equal(n.IPAM.Configs[0].Range, ""))
|
||||
assert.Check(t, is.Equal(len(n.IPAM.Configs[0].Reserved), 0))
|
||||
assert.Check(t, is.Equal(n.IPAM.Configs[0].Subnet, "192.168.1.0/24"))
|
||||
|
||||
ip := net.ParseIP(n.IPAM.Configs[0].Gateway)
|
||||
assert.Check(t, ip != nil)
|
||||
}
|
||||
|
||||
func TestAllocateWithOneSubnetGateway(t *testing.T) {
|
||||
na := newNetworkAllocator(t)
|
||||
n := &api.Network{
|
||||
ID: "testID",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test",
|
||||
},
|
||||
DriverConfig: &api.Driver{},
|
||||
IPAM: &api.IPAMOptions{
|
||||
Driver: &api.Driver{},
|
||||
Configs: []*api.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.1.0/24",
|
||||
Gateway: "192.168.1.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := na.Allocate(n)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Equal(len(n.IPAM.Configs), 1))
|
||||
assert.Check(t, is.Equal(n.IPAM.Configs[0].Range, ""))
|
||||
assert.Check(t, is.Equal(len(n.IPAM.Configs[0].Reserved), 0))
|
||||
assert.Check(t, is.Equal(n.IPAM.Configs[0].Subnet, "192.168.1.0/24"))
|
||||
assert.Check(t, is.Equal(n.IPAM.Configs[0].Gateway, "192.168.1.1"))
|
||||
}
|
||||
|
||||
func TestAllocateWithOneSubnetInvalidGateway(t *testing.T) {
|
||||
na := newNetworkAllocator(t)
|
||||
n := &api.Network{
|
||||
ID: "testID",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test",
|
||||
},
|
||||
DriverConfig: &api.Driver{},
|
||||
IPAM: &api.IPAMOptions{
|
||||
Driver: &api.Driver{},
|
||||
Configs: []*api.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.1.0/24",
|
||||
Gateway: "192.168.2.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := na.Allocate(n)
|
||||
assert.Check(t, is.ErrorContains(err, ""))
|
||||
}
|
||||
|
||||
// TestAllocateWithSmallSubnet validates that /32 subnets don't produce an error,
|
||||
// as /31 and /32 subnets are supported by docker daemon, starting with
|
||||
// https://github.com/moby/moby/commit/3a938df4b570aad3bfb4d5342379582e872fc1a3,
|
||||
func TestAllocateWithSmallSubnet(t *testing.T) {
|
||||
na := newNetworkAllocator(t)
|
||||
n := &api.Network{
|
||||
ID: "testID",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test",
|
||||
},
|
||||
DriverConfig: &api.Driver{},
|
||||
IPAM: &api.IPAMOptions{
|
||||
Driver: &api.Driver{},
|
||||
Configs: []*api.IPAMConfig{
|
||||
{
|
||||
Subnet: "1.1.1.1/32",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := na.Allocate(n)
|
||||
assert.Check(t, err)
|
||||
}
|
||||
|
||||
func TestAllocateWithTwoSubnetsNoGateway(t *testing.T) {
|
||||
na := newNetworkAllocator(t)
|
||||
n := &api.Network{
|
||||
ID: "testID",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test",
|
||||
},
|
||||
DriverConfig: &api.Driver{},
|
||||
IPAM: &api.IPAMOptions{
|
||||
Driver: &api.Driver{},
|
||||
Configs: []*api.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.1.0/24",
|
||||
},
|
||||
{
|
||||
Subnet: "192.168.2.0/24",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := na.Allocate(n)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Equal(len(n.IPAM.Configs), 2))
|
||||
assert.Check(t, is.Equal(n.IPAM.Configs[0].Range, ""))
|
||||
assert.Check(t, is.Equal(len(n.IPAM.Configs[0].Reserved), 0))
|
||||
assert.Check(t, is.Equal(n.IPAM.Configs[0].Subnet, "192.168.1.0/24"))
|
||||
assert.Check(t, is.Equal(n.IPAM.Configs[1].Range, ""))
|
||||
assert.Check(t, is.Equal(len(n.IPAM.Configs[1].Reserved), 0))
|
||||
assert.Check(t, is.Equal(n.IPAM.Configs[1].Subnet, "192.168.2.0/24"))
|
||||
|
||||
ip := net.ParseIP(n.IPAM.Configs[0].Gateway)
|
||||
assert.Check(t, ip != nil)
|
||||
ip = net.ParseIP(n.IPAM.Configs[1].Gateway)
|
||||
assert.Check(t, ip != nil)
|
||||
}
|
||||
|
||||
func TestFree(t *testing.T) {
|
||||
na := newNetworkAllocator(t)
|
||||
n := &api.Network{
|
||||
ID: "testID",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test",
|
||||
},
|
||||
DriverConfig: &api.Driver{},
|
||||
IPAM: &api.IPAMOptions{
|
||||
Driver: &api.Driver{},
|
||||
Configs: []*api.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.1.0/24",
|
||||
Gateway: "192.168.1.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := na.Allocate(n)
|
||||
assert.Check(t, err)
|
||||
|
||||
err = na.Deallocate(n)
|
||||
assert.Check(t, err)
|
||||
|
||||
// Reallocate again to make sure it succeeds.
|
||||
err = na.Allocate(n)
|
||||
assert.Check(t, err)
|
||||
}
|
||||
|
||||
func TestAllocateTaskFree(t *testing.T) {
|
||||
na1 := newNetworkAllocator(t)
|
||||
na2 := newNetworkAllocator(t)
|
||||
n1 := &api.Network{
|
||||
ID: "testID1",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test1",
|
||||
},
|
||||
DriverConfig: &api.Driver{},
|
||||
IPAM: &api.IPAMOptions{
|
||||
Driver: &api.Driver{},
|
||||
Configs: []*api.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.1.0/24",
|
||||
Gateway: "192.168.1.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
n2 := &api.Network{
|
||||
ID: "testID2",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test2",
|
||||
},
|
||||
DriverConfig: &api.Driver{},
|
||||
IPAM: &api.IPAMOptions{
|
||||
Driver: &api.Driver{},
|
||||
Configs: []*api.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.2.0/24",
|
||||
Gateway: "192.168.2.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
task1 := &api.Task{
|
||||
Networks: []*api.NetworkAttachment{
|
||||
{
|
||||
Network: n1,
|
||||
},
|
||||
{
|
||||
Network: n2,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
task2 := &api.Task{
|
||||
Networks: []*api.NetworkAttachment{
|
||||
{
|
||||
Network: n1,
|
||||
},
|
||||
{
|
||||
Network: n2,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := na1.Allocate(n1)
|
||||
assert.Check(t, err)
|
||||
|
||||
err = na1.Allocate(n2)
|
||||
assert.Check(t, err)
|
||||
|
||||
err = na1.AllocateTask(task1)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Equal(len(task1.Networks[0].Addresses), 1))
|
||||
assert.Check(t, is.Equal(len(task1.Networks[1].Addresses), 1))
|
||||
|
||||
_, subnet1, _ := net.ParseCIDR("192.168.1.0/24")
|
||||
_, subnet2, _ := net.ParseCIDR("192.168.2.0/24")
|
||||
|
||||
// variable coding: network/task/allocator
|
||||
ip111, _, err := net.ParseCIDR(task1.Networks[0].Addresses[0])
|
||||
assert.Check(t, err)
|
||||
|
||||
ip211, _, err := net.ParseCIDR(task1.Networks[1].Addresses[0])
|
||||
assert.Check(t, err)
|
||||
|
||||
assert.Check(t, is.Equal(subnet1.Contains(ip111), true))
|
||||
assert.Check(t, is.Equal(subnet2.Contains(ip211), true))
|
||||
|
||||
err = na1.AllocateTask(task2)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Equal(len(task2.Networks[0].Addresses), 1))
|
||||
assert.Check(t, is.Equal(len(task2.Networks[1].Addresses), 1))
|
||||
|
||||
ip121, _, err := net.ParseCIDR(task2.Networks[0].Addresses[0])
|
||||
assert.Check(t, err)
|
||||
|
||||
ip221, _, err := net.ParseCIDR(task2.Networks[1].Addresses[0])
|
||||
assert.Check(t, err)
|
||||
|
||||
assert.Check(t, is.Equal(subnet1.Contains(ip121), true))
|
||||
assert.Check(t, is.Equal(subnet2.Contains(ip221), true))
|
||||
|
||||
// Now allocate the same the same tasks in a second allocator
|
||||
// but intentionally in reverse order.
|
||||
err = na2.Allocate(n1)
|
||||
assert.Check(t, err)
|
||||
|
||||
err = na2.Allocate(n2)
|
||||
assert.Check(t, err)
|
||||
|
||||
err = na2.AllocateTask(task2)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Equal(len(task2.Networks[0].Addresses), 1))
|
||||
assert.Check(t, is.Equal(len(task2.Networks[1].Addresses), 1))
|
||||
|
||||
ip122, _, err := net.ParseCIDR(task2.Networks[0].Addresses[0])
|
||||
assert.Check(t, err)
|
||||
|
||||
ip222, _, err := net.ParseCIDR(task2.Networks[1].Addresses[0])
|
||||
assert.Check(t, err)
|
||||
|
||||
assert.Check(t, is.Equal(subnet1.Contains(ip122), true))
|
||||
assert.Check(t, is.Equal(subnet2.Contains(ip222), true))
|
||||
assert.Check(t, is.DeepEqual(ip121, ip122))
|
||||
assert.Check(t, is.DeepEqual(ip221, ip222))
|
||||
|
||||
err = na2.AllocateTask(task1)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Equal(len(task1.Networks[0].Addresses), 1))
|
||||
assert.Check(t, is.Equal(len(task1.Networks[1].Addresses), 1))
|
||||
|
||||
ip112, _, err := net.ParseCIDR(task1.Networks[0].Addresses[0])
|
||||
assert.Check(t, err)
|
||||
|
||||
ip212, _, err := net.ParseCIDR(task1.Networks[1].Addresses[0])
|
||||
assert.Check(t, err)
|
||||
|
||||
assert.Check(t, is.Equal(subnet1.Contains(ip112), true))
|
||||
assert.Check(t, is.Equal(subnet2.Contains(ip212), true))
|
||||
assert.Check(t, is.DeepEqual(ip111, ip112))
|
||||
assert.Check(t, is.DeepEqual(ip211, ip212))
|
||||
|
||||
// Deallocate task
|
||||
err = na1.DeallocateTask(task1)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Equal(len(task1.Networks[0].Addresses), 0))
|
||||
assert.Check(t, is.Equal(len(task1.Networks[1].Addresses), 0))
|
||||
|
||||
// Try allocation after free
|
||||
err = na1.AllocateTask(task1)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Equal(len(task1.Networks[0].Addresses), 1))
|
||||
assert.Check(t, is.Equal(len(task1.Networks[1].Addresses), 1))
|
||||
|
||||
ip111, _, err = net.ParseCIDR(task1.Networks[0].Addresses[0])
|
||||
assert.Check(t, err)
|
||||
|
||||
ip211, _, err = net.ParseCIDR(task1.Networks[1].Addresses[0])
|
||||
assert.Check(t, err)
|
||||
|
||||
assert.Check(t, is.Equal(subnet1.Contains(ip111), true))
|
||||
assert.Check(t, is.Equal(subnet2.Contains(ip211), true))
|
||||
|
||||
err = na1.DeallocateTask(task1)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Equal(len(task1.Networks[0].Addresses), 0))
|
||||
assert.Check(t, is.Equal(len(task1.Networks[1].Addresses), 0))
|
||||
|
||||
// Try to free endpoints on an already freed task
|
||||
err = na1.DeallocateTask(task1)
|
||||
assert.Check(t, err)
|
||||
}
|
||||
|
||||
func TestAllocateService(t *testing.T) {
|
||||
na := newNetworkAllocator(t)
|
||||
n := &api.Network{
|
||||
ID: "testID",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
s := &api.Service{
|
||||
ID: "testID1",
|
||||
Spec: api.ServiceSpec{
|
||||
Task: api.TaskSpec{
|
||||
Networks: []*api.NetworkAttachmentConfig{
|
||||
{
|
||||
Target: "testID",
|
||||
},
|
||||
},
|
||||
},
|
||||
Endpoint: &api.EndpointSpec{
|
||||
Ports: []*api.PortConfig{
|
||||
{
|
||||
Name: "http",
|
||||
TargetPort: 80,
|
||||
},
|
||||
{
|
||||
Name: "https",
|
||||
TargetPort: 443,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := na.Allocate(n)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, n.IPAM.Configs != nil)
|
||||
assert.Check(t, is.Equal(len(n.IPAM.Configs), 1))
|
||||
assert.Check(t, is.Equal(n.IPAM.Configs[0].Range, ""))
|
||||
assert.Check(t, is.Equal(len(n.IPAM.Configs[0].Reserved), 0))
|
||||
|
||||
_, subnet, err := net.ParseCIDR(n.IPAM.Configs[0].Subnet)
|
||||
assert.Check(t, err)
|
||||
|
||||
gwip := net.ParseIP(n.IPAM.Configs[0].Gateway)
|
||||
assert.Check(t, gwip != nil)
|
||||
|
||||
err = na.AllocateService(s)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Len(s.Endpoint.Ports, 0)) // Network allocator is not responsible for allocating ports.
|
||||
|
||||
assert.Check(t, is.Equal(1, len(s.Endpoint.VirtualIPs)))
|
||||
|
||||
assert.Check(t, is.DeepEqual(s.Endpoint.Spec, s.Spec.Endpoint))
|
||||
|
||||
ip, _, err := net.ParseCIDR(s.Endpoint.VirtualIPs[0].Addr)
|
||||
assert.Check(t, err)
|
||||
|
||||
assert.Check(t, is.Equal(true, subnet.Contains(ip)))
|
||||
}
|
||||
|
||||
func TestDeallocateServiceAllocateIngressMode(t *testing.T) {
|
||||
na := newNetworkAllocator(t)
|
||||
|
||||
n := &api.Network{
|
||||
ID: "testNetID1",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test",
|
||||
},
|
||||
Ingress: true,
|
||||
},
|
||||
}
|
||||
|
||||
err := na.Allocate(n)
|
||||
assert.Check(t, err)
|
||||
|
||||
s := &api.Service{
|
||||
ID: "testID1",
|
||||
Spec: api.ServiceSpec{
|
||||
Endpoint: &api.EndpointSpec{
|
||||
Ports: []*api.PortConfig{
|
||||
{
|
||||
Name: "some_tcp",
|
||||
TargetPort: 1234,
|
||||
PublishedPort: 1234,
|
||||
PublishMode: api.PublishModeIngress,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Endpoint: &api.Endpoint{},
|
||||
}
|
||||
|
||||
s.Endpoint.VirtualIPs = append(s.Endpoint.VirtualIPs,
|
||||
&api.Endpoint_VirtualIP{NetworkID: n.ID})
|
||||
|
||||
err = na.AllocateService(s)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 1))
|
||||
|
||||
err = na.DeallocateService(s)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Len(s.Endpoint.Ports, 0))
|
||||
assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 0))
|
||||
// Allocate again.
|
||||
s.Endpoint.VirtualIPs = append(s.Endpoint.VirtualIPs,
|
||||
&api.Endpoint_VirtualIP{NetworkID: n.ID})
|
||||
|
||||
err = na.AllocateService(s)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 1))
|
||||
}
|
||||
|
||||
func TestServiceNetworkUpdate(t *testing.T) {
|
||||
na := newNetworkAllocator(t)
|
||||
|
||||
n1 := &api.Network{
|
||||
ID: "testID1",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
n2 := &api.Network{
|
||||
ID: "testID2",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Allocate both networks
|
||||
err := na.Allocate(n1)
|
||||
assert.Check(t, err)
|
||||
|
||||
err = na.Allocate(n2)
|
||||
assert.Check(t, err)
|
||||
|
||||
// Attach a network to a service spec nd allocate a service
|
||||
s := &api.Service{
|
||||
ID: "testID1",
|
||||
Spec: api.ServiceSpec{
|
||||
Task: api.TaskSpec{
|
||||
Networks: []*api.NetworkAttachmentConfig{
|
||||
{
|
||||
Target: "testID1",
|
||||
},
|
||||
},
|
||||
},
|
||||
Endpoint: &api.EndpointSpec{
|
||||
Mode: api.ResolutionModeVirtualIP,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = na.AllocateService(s)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, na.IsServiceAllocated(s))
|
||||
assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 1))
|
||||
|
||||
// Now update the same service with another network
|
||||
s.Spec.Task.Networks = append(s.Spec.Task.Networks, &api.NetworkAttachmentConfig{Target: "testID2"})
|
||||
|
||||
assert.Check(t, !na.IsServiceAllocated(s))
|
||||
err = na.AllocateService(s)
|
||||
assert.Check(t, err)
|
||||
|
||||
assert.Check(t, na.IsServiceAllocated(s))
|
||||
assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 2))
|
||||
|
||||
s.Spec.Task.Networks = s.Spec.Task.Networks[:1]
|
||||
|
||||
// Check if service needs update and allocate with updated service spec
|
||||
assert.Check(t, !na.IsServiceAllocated(s))
|
||||
|
||||
err = na.AllocateService(s)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, na.IsServiceAllocated(s))
|
||||
assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 1))
|
||||
|
||||
s.Spec.Task.Networks = s.Spec.Task.Networks[:0]
|
||||
// Check if service needs update with all the networks removed and allocate with updated service spec
|
||||
assert.Check(t, !na.IsServiceAllocated(s))
|
||||
|
||||
err = na.AllocateService(s)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, na.IsServiceAllocated(s))
|
||||
assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 0))
|
||||
|
||||
// Attach a network and allocate service
|
||||
s.Spec.Task.Networks = append(s.Spec.Task.Networks, &api.NetworkAttachmentConfig{Target: "testID2"})
|
||||
assert.Check(t, !na.IsServiceAllocated(s))
|
||||
|
||||
err = na.AllocateService(s)
|
||||
assert.Check(t, err)
|
||||
|
||||
assert.Check(t, na.IsServiceAllocated(s))
|
||||
assert.Check(t, is.Len(s.Endpoint.VirtualIPs, 1))
|
||||
|
||||
}
|
||||
|
||||
type mockIpam struct {
|
||||
actualIpamOptions map[string]string
|
||||
}
|
||||
|
||||
func (a *mockIpam) GetDefaultAddressSpaces() (string, string, error) {
|
||||
return "defaultAS", "defaultAS", nil
|
||||
}
|
||||
|
||||
func (a *mockIpam) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
|
||||
a.actualIpamOptions = options
|
||||
|
||||
poolCidr, _ := types.ParseCIDR(pool)
|
||||
return fmt.Sprintf("%s/%s", "defaultAS", pool), poolCidr, nil, nil
|
||||
}
|
||||
|
||||
func (a *mockIpam) ReleasePool(poolID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *mockIpam) RequestAddress(poolID string, ip net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (a *mockIpam) ReleaseAddress(poolID string, ip net.IP) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *mockIpam) IsBuiltIn() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func TestCorrectlyPassIPAMOptions(t *testing.T) {
|
||||
var err error
|
||||
expectedIpamOptions := map[string]string{"network-name": "freddie"}
|
||||
|
||||
na := newNetworkAllocator(t)
|
||||
ipamDriver := &mockIpam{}
|
||||
|
||||
err = na.(*cnmNetworkAllocator).ipamRegistry.RegisterIpamDriver("mockipam", ipamDriver)
|
||||
assert.Check(t, err)
|
||||
|
||||
n := &api.Network{
|
||||
ID: "testID",
|
||||
Spec: api.NetworkSpec{
|
||||
Annotations: api.Annotations{
|
||||
Name: "test",
|
||||
},
|
||||
DriverConfig: &api.Driver{},
|
||||
IPAM: &api.IPAMOptions{
|
||||
Driver: &api.Driver{
|
||||
Name: "mockipam",
|
||||
Options: expectedIpamOptions,
|
||||
},
|
||||
Configs: []*api.IPAMConfig{
|
||||
{
|
||||
Subnet: "192.168.1.0/24",
|
||||
Gateway: "192.168.1.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
err = na.Allocate(n)
|
||||
|
||||
assert.Check(t, is.DeepEqual(expectedIpamOptions, ipamDriver.actualIpamOptions))
|
||||
assert.Check(t, err)
|
||||
}
|
||||
91
libnetwork/cnmallocator/provider.go
Normal file
91
libnetwork/cnmallocator/provider.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package cnmallocator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/libnetwork/driverapi"
|
||||
"github.com/docker/docker/libnetwork/drivers/overlay/overlayutils"
|
||||
"github.com/docker/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/docker/pkg/plugingetter"
|
||||
"github.com/moby/swarmkit/v2/api"
|
||||
"github.com/moby/swarmkit/v2/manager/allocator/networkallocator"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type Provider struct {
|
||||
pg plugingetter.PluginGetter
|
||||
}
|
||||
|
||||
var _ networkallocator.Provider = &Provider{}
|
||||
|
||||
// NewProvider returns a new cnmallocator provider.
|
||||
func NewProvider(pg plugingetter.PluginGetter) *Provider {
|
||||
return &Provider{pg: pg}
|
||||
}
|
||||
|
||||
// ValidateIPAMDriver implements networkallocator.NetworkProvider.
|
||||
func (p *Provider) ValidateIPAMDriver(driver *api.Driver) error {
|
||||
if driver == nil {
|
||||
// It is ok to not specify the driver. We will choose
|
||||
// a default driver.
|
||||
return nil
|
||||
}
|
||||
|
||||
if driver.Name == "" {
|
||||
return status.Errorf(codes.InvalidArgument, "driver name: if driver is specified name is required")
|
||||
}
|
||||
if strings.ToLower(driver.Name) == ipamapi.DefaultIPAM {
|
||||
return nil
|
||||
}
|
||||
return p.validatePluginDriver(driver, ipamapi.PluginEndpointType)
|
||||
}
|
||||
|
||||
// ValidateIngressNetworkDriver implements networkallocator.NetworkProvider.
|
||||
func (p *Provider) ValidateIngressNetworkDriver(driver *api.Driver) error {
|
||||
if driver != nil && driver.Name != "overlay" {
|
||||
return status.Errorf(codes.Unimplemented, "only overlay driver is currently supported for ingress network")
|
||||
}
|
||||
return p.ValidateNetworkDriver(driver)
|
||||
}
|
||||
|
||||
// ValidateNetworkDriver implements networkallocator.NetworkProvider.
|
||||
func (p *Provider) ValidateNetworkDriver(driver *api.Driver) error {
|
||||
if driver == nil {
|
||||
// It is ok to not specify the driver. We will choose
|
||||
// a default driver.
|
||||
return nil
|
||||
}
|
||||
|
||||
if driver.Name == "" {
|
||||
return status.Errorf(codes.InvalidArgument, "driver name: if driver is specified name is required")
|
||||
}
|
||||
|
||||
// First check against the known drivers
|
||||
if IsBuiltInDriver(driver.Name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.validatePluginDriver(driver, driverapi.NetworkPluginEndpointType)
|
||||
}
|
||||
|
||||
func (p *Provider) validatePluginDriver(driver *api.Driver, pluginType string) error {
|
||||
if p.pg == nil {
|
||||
return status.Errorf(codes.InvalidArgument, "plugin %s not supported", driver.Name)
|
||||
}
|
||||
|
||||
plug, err := p.pg.Get(driver.Name, pluginType, plugingetter.Lookup)
|
||||
if err != nil {
|
||||
return status.Errorf(codes.InvalidArgument, "error during lookup of plugin %s", driver.Name)
|
||||
}
|
||||
|
||||
if plug.IsV1() {
|
||||
return status.Errorf(codes.InvalidArgument, "legacy plugin %s of type %s is not supported in swarm mode", driver.Name, pluginType)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) SetDefaultVXLANUDPPort(port uint32) error {
|
||||
return overlayutils.ConfigVXLANUDPPort(port)
|
||||
}
|
||||
31
libnetwork/cnmallocator/provider_test.go
Normal file
31
libnetwork/cnmallocator/provider_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package cnmallocator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/moby/swarmkit/v2/api"
|
||||
"github.com/moby/swarmkit/v2/testutils"
|
||||
"google.golang.org/grpc/codes"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestValidateDriver(t *testing.T) {
|
||||
p := NewProvider(nil)
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
validator func(*api.Driver) error
|
||||
}{
|
||||
{"IPAM", p.ValidateIPAMDriver},
|
||||
{"Network", p.ValidateNetworkDriver},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Check(t, tt.validator(nil))
|
||||
|
||||
err := tt.validator(&api.Driver{Name: ""})
|
||||
assert.Check(t, is.ErrorContains(err, ""))
|
||||
assert.Check(t, is.Equal(codes.InvalidArgument, testutils.ErrorCode(err)))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/docker/docker/errdefs"
|
||||
@@ -73,18 +74,20 @@ func (i *bridgeInterface) addresses(family int) ([]netlink.Addr, error) {
|
||||
func getRequiredIPv6Addrs(config *networkConfiguration) (requiredAddrs map[netip.Addr]netip.Prefix, err error) {
|
||||
requiredAddrs = make(map[netip.Addr]netip.Prefix)
|
||||
|
||||
// Always give the bridge 'fe80::1' - every interface is required to have an
|
||||
// address in 'fe80::/64'. Linux may assign an address, but we'll replace it with
|
||||
// 'fe80::1'. Then, if the configured prefix is 'fe80::/64', the IPAM pool
|
||||
// assigned address will not be a second address in the LL subnet.
|
||||
ra, ok := netiputil.ToPrefix(bridgeIPv6)
|
||||
if !ok {
|
||||
err = fmt.Errorf("Failed to convert Link-Local IPv6 address to netip.Prefix")
|
||||
return nil, err
|
||||
if os.Getenv("DOCKER_BRIDGE_PRESERVE_KERNEL_LL") != "1" {
|
||||
// Always give the bridge 'fe80::1' - every interface is required to have an
|
||||
// address in 'fe80::/64'. Linux may assign an address, but we'll replace it with
|
||||
// 'fe80::1'. Then, if the configured prefix is 'fe80::/64', the IPAM pool
|
||||
// assigned address will not be a second address in the LL subnet.
|
||||
ra, ok := netiputil.ToPrefix(bridgeIPv6)
|
||||
if !ok {
|
||||
err = fmt.Errorf("Failed to convert Link-Local IPv6 address to netip.Prefix")
|
||||
return nil, err
|
||||
}
|
||||
requiredAddrs[ra.Addr()] = ra
|
||||
}
|
||||
requiredAddrs[ra.Addr()] = ra
|
||||
|
||||
ra, ok = netiputil.ToPrefix(config.AddressIPv6)
|
||||
ra, ok := netiputil.ToPrefix(config.AddressIPv6)
|
||||
if !ok {
|
||||
err = fmt.Errorf("failed to convert bridge IPv6 address '%s' to netip.Prefix", config.AddressIPv6.String())
|
||||
return nil, err
|
||||
@@ -116,6 +119,14 @@ func (i *bridgeInterface) programIPv6Addresses(config *networkConfiguration) err
|
||||
if !ok {
|
||||
return errdefs.System(fmt.Errorf("Failed to convert IPv6 address '%s' to netip.Addr", config.AddressIPv6))
|
||||
}
|
||||
// Optionally, avoid deleting the kernel-assigned link local address.
|
||||
// (Don't delete fe80::1 either - if it was previously assigned to the bridge, and the
|
||||
// kernel_ll address was deleted, the bridge won't get a new kernel_ll address.)
|
||||
if os.Getenv("DOCKER_BRIDGE_PRESERVE_KERNEL_LL") == "1" {
|
||||
if p, _ := ea.Prefix(64); p == linkLocalPrefix {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Ignore the prefix length when comparing addresses, it's informational
|
||||
// (RFC-5942 section 4), and removing/re-adding an address that's still valid
|
||||
// would disrupt traffic on live-restore.
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
"github.com/containerd/log"
|
||||
@@ -13,6 +14,9 @@ import (
|
||||
// bridgeIPv6 is the default, link-local IPv6 address for the bridge (fe80::1/64)
|
||||
var bridgeIPv6 = &net.IPNet{IP: net.ParseIP("fe80::1"), Mask: net.CIDRMask(64, 128)}
|
||||
|
||||
// Standard link local prefix
|
||||
var linkLocalPrefix = netip.MustParsePrefix("fe80::/64")
|
||||
|
||||
const (
|
||||
ipv6ForwardConfPerm = 0o644
|
||||
ipv6ForwardConfDefault = "/proc/sys/net/ipv6/conf/default/forwarding"
|
||||
|
||||
@@ -67,7 +67,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
|
||||
|
||||
veth, err := nlh.LinkByName(overlayIfName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cound not find link by name %s: %v", overlayIfName, err)
|
||||
return fmt.Errorf("could not find link by name %s: %v", overlayIfName, err)
|
||||
}
|
||||
err = nlh.LinkSetMTU(veth, mtu)
|
||||
if err != nil {
|
||||
|
||||
@@ -406,7 +406,7 @@ func parseStaticRoutes(r api.JoinResponse) ([]*types.StaticRoute, error) {
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
// parseInterfaces validates all the parameters of an Interface and returns them.
|
||||
// parseInterface validates all the parameters of an Interface and returns them.
|
||||
func parseInterface(r api.CreateEndpointResponse) (*api.Interface, error) {
|
||||
var outIf *api.Interface
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/internal/sliceutil"
|
||||
"github.com/docker/docker/libnetwork/datastore"
|
||||
"github.com/docker/docker/libnetwork/ipamapi"
|
||||
@@ -543,6 +544,10 @@ func (ep *Endpoint) sbJoin(sb *Sandbox, options ...EndpointOption) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = addEpToResolver(context.TODO(), n.Name(), ep.Name(), &sb.config, ep.iface, n.Resolvers()); err != nil {
|
||||
return errdefs.System(err)
|
||||
}
|
||||
|
||||
if err = n.getController().updateToStore(ep); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -745,6 +750,10 @@ func (ep *Endpoint) sbLeave(sb *Sandbox, force bool) error {
|
||||
log.G(context.TODO()).Warnf("Failed to clean up service info on container %s disconnect: %v", ep.name, err)
|
||||
}
|
||||
|
||||
if err := deleteEpFromResolver(ep.Name(), ep.iface, n.Resolvers()); err != nil {
|
||||
log.G(context.TODO()).Warnf("Failed to clean up resolver info on container %s disconnect: %v", ep.name, err)
|
||||
}
|
||||
|
||||
if err := sb.clearNetworkResources(ep); err != nil {
|
||||
log.G(context.TODO()).Warnf("Failed to clean up network resources on container %s disconnect: %v", ep.name, err)
|
||||
}
|
||||
|
||||
@@ -51,3 +51,11 @@ func SubnetRange(network, subnet netip.Prefix) (start, end uint64) {
|
||||
end = start + (1 << uint64(subnet.Addr().BitLen()-subnet.Bits())) - 1
|
||||
return start, end
|
||||
}
|
||||
|
||||
// AddrPortFromNet converts a net.Addr into a netip.AddrPort.
|
||||
func AddrPortFromNet(addr net.Addr) netip.AddrPort {
|
||||
if a, ok := addr.(interface{ AddrPort() netip.AddrPort }); ok {
|
||||
return a.AddrPort()
|
||||
}
|
||||
return netip.AddrPort{}
|
||||
}
|
||||
|
||||
@@ -192,7 +192,6 @@ type Network struct {
|
||||
dbExists bool
|
||||
persist bool
|
||||
drvOnce *sync.Once
|
||||
resolverOnce sync.Once //nolint:nolintlint,unused // only used on windows
|
||||
resolver []*Resolver
|
||||
internal bool
|
||||
attachable bool
|
||||
@@ -204,6 +203,7 @@ type Network struct {
|
||||
configFrom string
|
||||
loadBalancerIP net.IP
|
||||
loadBalancerMode string
|
||||
platformNetwork //nolint:nolintlint,unused // only populated on windows
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
@@ -244,6 +244,13 @@ func (n *Network) Type() string {
|
||||
return n.networkType
|
||||
}
|
||||
|
||||
func (n *Network) Resolvers() []*Resolver {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
return n.resolver
|
||||
}
|
||||
|
||||
func (n *Network) Key() []string {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
@@ -2097,10 +2104,6 @@ func (n *Network) ResolveService(ctx context.Context, name string) ([]*net.SRV,
|
||||
return srv, ip
|
||||
}
|
||||
|
||||
func (n *Network) ExecFunc(f func()) error {
|
||||
return types.NotImplementedErrorf("ExecFunc not supported by network")
|
||||
}
|
||||
|
||||
func (n *Network) NdotsSet() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -2,13 +2,33 @@
|
||||
|
||||
package libnetwork
|
||||
|
||||
import "github.com/docker/docker/libnetwork/ipamapi"
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/libnetwork/ipamapi"
|
||||
)
|
||||
|
||||
type platformNetwork struct{} //nolint:nolintlint,unused // only populated on windows
|
||||
|
||||
// Stub implementations for DNS related functions
|
||||
|
||||
func (n *Network) startResolver() {
|
||||
}
|
||||
|
||||
func addEpToResolver(
|
||||
ctx context.Context,
|
||||
netName, epName string,
|
||||
config *containerConfig,
|
||||
epIface *EndpointInterface,
|
||||
resolvers []*Resolver,
|
||||
) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteEpFromResolver(epName string, epIface *EndpointInterface, resolvers []*Resolver) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func defaultIpamForNetworkType(networkType string) string {
|
||||
return ipamapi.DefaultIPAM
|
||||
}
|
||||
|
||||
@@ -4,7 +4,12 @@ package libnetwork
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Microsoft/hcsshim"
|
||||
@@ -12,8 +17,14 @@ import (
|
||||
"github.com/docker/docker/libnetwork/drivers/windows"
|
||||
"github.com/docker/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/docker/libnetwork/ipams/windowsipam"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type platformNetwork struct {
|
||||
resolverOnce sync.Once
|
||||
dnsCompartment uint32
|
||||
}
|
||||
|
||||
func executeInCompartment(compartmentID uint32, x func()) {
|
||||
runtime.LockOSThread()
|
||||
|
||||
@@ -28,6 +39,11 @@ func executeInCompartment(compartmentID uint32, x func()) {
|
||||
x()
|
||||
}
|
||||
|
||||
func (n *Network) ExecFunc(f func()) error {
|
||||
executeInCompartment(n.dnsCompartment, f)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Network) startResolver() {
|
||||
if n.networkType == "ics" {
|
||||
return
|
||||
@@ -48,9 +64,10 @@ func (n *Network) startResolver() {
|
||||
for _, subnet := range hnsresponse.Subnets {
|
||||
if subnet.GatewayAddress != "" {
|
||||
for i := 0; i < 3; i++ {
|
||||
resolver := NewResolver(subnet.GatewayAddress, false, n)
|
||||
resolver := NewResolver(subnet.GatewayAddress, true, n)
|
||||
log.G(context.TODO()).Debugf("Binding a resolver on network %s gateway %s", n.Name(), subnet.GatewayAddress)
|
||||
executeInCompartment(hnsresponse.DNSServerCompartment, resolver.SetupFunc(53))
|
||||
n.dnsCompartment = hnsresponse.DNSServerCompartment
|
||||
n.ExecFunc(resolver.SetupFunc(53))
|
||||
|
||||
if err = resolver.Start(); err != nil {
|
||||
log.G(context.TODO()).Errorf("Resolver Setup/Start failed for container %s, %q", n.Name(), err)
|
||||
@@ -66,6 +83,166 @@ func (n *Network) startResolver() {
|
||||
})
|
||||
}
|
||||
|
||||
// addEpToResolver configures the internal DNS resolver for an endpoint.
|
||||
//
|
||||
// Windows resolvers don't consistently fall back to a secondary server if they
|
||||
// get a SERVFAIL from our resolver. So, our resolver needs to forward the query
|
||||
// upstream.
|
||||
//
|
||||
// To retrieve the list of DNS Servers to use for requests originating from an
|
||||
// endpoint, this method finds the HNSEndpoint represented by the endpoint. If
|
||||
// HNSEndpoint's list of DNS servers includes the HNSEndpoint's gateway address,
|
||||
// it's the Resolver running at that address. Other DNS servers in the
|
||||
// list have either come from config ('--dns') or have been set up by HNS as
|
||||
// external resolvers, these are the external servers the Resolver should
|
||||
// use for DNS requests from that endpoint.
|
||||
func addEpToResolver(
|
||||
ctx context.Context,
|
||||
netName, epName string,
|
||||
config *containerConfig,
|
||||
epIface *EndpointInterface,
|
||||
resolvers []*Resolver,
|
||||
) error {
|
||||
if config.dnsNoProxy {
|
||||
return nil
|
||||
}
|
||||
hnsEndpoints, err := hcsshim.HNSListEndpointRequest()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return addEpToResolverImpl(ctx, netName, epName, epIface, resolvers, hnsEndpoints)
|
||||
}
|
||||
|
||||
func addEpToResolverImpl(
|
||||
ctx context.Context,
|
||||
netName, epName string,
|
||||
epIface *EndpointInterface,
|
||||
resolvers []*Resolver,
|
||||
hnsEndpoints []hcsshim.HNSEndpoint,
|
||||
) error {
|
||||
// Find the HNSEndpoint represented by ep, matching on endpoint address.
|
||||
hnsEp := findHNSEp(epIface.addr, epIface.addrv6, hnsEndpoints)
|
||||
if hnsEp == nil || !hnsEp.EnableInternalDNS {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find the resolver for that HNSEndpoint, matching on gateway address.
|
||||
resolver := findResolver(resolvers, hnsEp.GatewayAddress, hnsEp.GatewayAddressV6)
|
||||
if resolver == nil {
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"network": netName,
|
||||
"endpoint": epName,
|
||||
}).Debug("No internal DNS resolver to configure")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the list of DNS servers HNS has set up for this Endpoint.
|
||||
var dnsList []extDNSEntry
|
||||
dnsServers := strings.Split(hnsEp.DNSServerList, ",")
|
||||
|
||||
// Create an extDNSEntry for each DNS server, apart from 'resolver' itself.
|
||||
var foundSelf bool
|
||||
hnsGw4, _ := netip.ParseAddr(hnsEp.GatewayAddress)
|
||||
hnsGw6, _ := netip.ParseAddr(hnsEp.GatewayAddressV6)
|
||||
for _, dnsServer := range dnsServers {
|
||||
dnsAddr, _ := netip.ParseAddr(dnsServer)
|
||||
if dnsAddr.IsValid() && (dnsAddr == hnsGw4 || dnsAddr == hnsGw6) {
|
||||
foundSelf = true
|
||||
} else {
|
||||
dnsList = append(dnsList, extDNSEntry{IPStr: dnsServer})
|
||||
}
|
||||
}
|
||||
if !foundSelf {
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"network": netName,
|
||||
"endpoint": epName,
|
||||
}).Debug("Endpoint is not configured to use internal DNS resolver")
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the internal resolver is configured as one of this endpoint's DNS servers,
|
||||
// tell it which ext servers to use for requests from this endpoint's addresses.
|
||||
log.G(ctx).Infof("External DNS servers for '%s': %v", epName, dnsList)
|
||||
if srcAddr, ok := netip.AddrFromSlice(hnsEp.IPAddress); ok {
|
||||
if err := resolver.SetExtServersForSrc(srcAddr.Unmap(), dnsList); err != nil {
|
||||
return errors.Wrapf(err, "failed to set external DNS servers for %s address %s",
|
||||
epName, hnsEp.IPAddress)
|
||||
}
|
||||
}
|
||||
if srcAddr, ok := netip.AddrFromSlice(hnsEp.IPv6Address); ok {
|
||||
if err := resolver.SetExtServersForSrc(srcAddr, dnsList); err != nil {
|
||||
return errors.Wrapf(err, "failed to set external DNS servers for %s address %s",
|
||||
epName, hnsEp.IPv6Address)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteEpFromResolver(epName string, epIface *EndpointInterface, resolvers []*Resolver) error {
|
||||
hnsEndpoints, err := hcsshim.HNSListEndpointRequest()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return deleteEpFromResolverImpl(epName, epIface, resolvers, hnsEndpoints)
|
||||
}
|
||||
|
||||
func deleteEpFromResolverImpl(
|
||||
epName string,
|
||||
epIface *EndpointInterface,
|
||||
resolvers []*Resolver,
|
||||
hnsEndpoints []hcsshim.HNSEndpoint,
|
||||
) error {
|
||||
// Find the HNSEndpoint represented by ep, matching on endpoint address.
|
||||
hnsEp := findHNSEp(epIface.addr, epIface.addrv6, hnsEndpoints)
|
||||
if hnsEp == nil {
|
||||
return fmt.Errorf("no HNS endpoint for %s", epName)
|
||||
}
|
||||
|
||||
// Find the resolver for that HNSEndpoint, matching on gateway address.
|
||||
resolver := findResolver(resolvers, hnsEp.GatewayAddress, hnsEp.GatewayAddressV6)
|
||||
if resolver == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete external DNS servers for the endpoint's IP addresses.
|
||||
if srcAddr, ok := netip.AddrFromSlice(hnsEp.IPAddress); ok {
|
||||
if err := resolver.SetExtServersForSrc(srcAddr.Unmap(), nil); err != nil {
|
||||
return errors.Wrapf(err, "failed to delete external DNS servers for %s address %s",
|
||||
epName, hnsEp.IPv6Address)
|
||||
}
|
||||
}
|
||||
if srcAddr, ok := netip.AddrFromSlice(hnsEp.IPv6Address); ok {
|
||||
if err := resolver.SetExtServersForSrc(srcAddr, nil); err != nil {
|
||||
return errors.Wrapf(err, "failed to delete external DNS servers for %s address %s",
|
||||
epName, hnsEp.IPv6Address)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findHNSEp(ip4, ip6 *net.IPNet, hnsEndpoints []hcsshim.HNSEndpoint) *hcsshim.HNSEndpoint {
|
||||
for _, hnsEp := range hnsEndpoints {
|
||||
if (hnsEp.IPAddress != nil && hnsEp.IPAddress.Equal(ip4.IP)) ||
|
||||
(hnsEp.IPv6Address != nil && hnsEp.IPv6Address.Equal(ip6.IP)) {
|
||||
return &hnsEp
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findResolver(resolvers []*Resolver, gw4, gw6 string) *Resolver {
|
||||
gw4addr, _ := netip.ParseAddr(gw4)
|
||||
gw6addr, _ := netip.ParseAddr(gw6)
|
||||
for _, resolver := range resolvers {
|
||||
ns := resolver.NameServer()
|
||||
if ns.IsValid() && (ns == gw4addr || ns == gw6addr) {
|
||||
return resolver
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func defaultIpamForNetworkType(networkType string) string {
|
||||
if windows.IsBuiltinLocalDriver(networkType) {
|
||||
return windowsipam.DefaultIPAM
|
||||
|
||||
201
libnetwork/network_windows_test.go
Normal file
201
libnetwork/network_windows_test.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package libnetwork
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/Microsoft/hcsshim"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestAddEpToResolver(t *testing.T) {
|
||||
const (
|
||||
ep1v4 = "192.0.2.11"
|
||||
ep2v4 = "192.0.2.12"
|
||||
epFiveDNS = "192.0.2.13"
|
||||
epNoIntDNS = "192.0.2.14"
|
||||
ep1v6 = "2001:db8:aaaa::2"
|
||||
gw1v4 = "192.0.2.1"
|
||||
gw2v4 = "192.0.2.2"
|
||||
gw1v6 = "2001:db8:aaaa::1"
|
||||
dns1v4 = "198.51.100.1"
|
||||
dns2v4 = "198.51.100.2"
|
||||
dns3v4 = "198.51.100.3"
|
||||
)
|
||||
hnsEndpoints := map[string]hcsshim.HNSEndpoint{
|
||||
ep1v4: {
|
||||
IPAddress: net.ParseIP(ep1v4),
|
||||
GatewayAddress: gw1v4,
|
||||
DNSServerList: gw1v4 + "," + dns1v4,
|
||||
EnableInternalDNS: true,
|
||||
},
|
||||
ep2v4: {
|
||||
IPAddress: net.ParseIP(ep2v4),
|
||||
GatewayAddress: gw1v4,
|
||||
DNSServerList: gw1v4 + "," + dns2v4,
|
||||
EnableInternalDNS: true,
|
||||
},
|
||||
epFiveDNS: {
|
||||
IPAddress: net.ParseIP(epFiveDNS),
|
||||
GatewayAddress: gw1v4,
|
||||
DNSServerList: gw1v4 + "," + dns1v4 + "," + dns2v4 + "," + dns3v4 + ",198.51.100.4",
|
||||
EnableInternalDNS: true,
|
||||
},
|
||||
epNoIntDNS: {
|
||||
IPAddress: net.ParseIP(epNoIntDNS),
|
||||
GatewayAddress: gw1v4,
|
||||
DNSServerList: gw1v4 + "," + dns1v4,
|
||||
//EnableInternalDNS: false,
|
||||
},
|
||||
ep1v6: {
|
||||
IPv6Address: net.ParseIP(ep1v6),
|
||||
GatewayAddressV6: gw1v6,
|
||||
DNSServerList: gw1v6 + "," + dns1v4,
|
||||
EnableInternalDNS: true,
|
||||
},
|
||||
}
|
||||
|
||||
makeIPNet := func(addr, netmask string) *net.IPNet {
|
||||
t.Helper()
|
||||
ip, ipnet, err := net.ParseCIDR(addr + "/" + netmask)
|
||||
assert.NilError(t, err)
|
||||
return &net.IPNet{IP: ip, Mask: ipnet.Mask}
|
||||
}
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
epToAdd *EndpointInterface
|
||||
hnsEndpoints []hcsshim.HNSEndpoint
|
||||
resolverLAs []string
|
||||
expIPToExtDNS map[netip.Addr][maxExtDNS]extDNSEntry
|
||||
expResolverIdx int
|
||||
}{
|
||||
{
|
||||
name: "ipv4",
|
||||
epToAdd: &EndpointInterface{
|
||||
addr: makeIPNet(ep1v4, "32"),
|
||||
},
|
||||
hnsEndpoints: []hcsshim.HNSEndpoint{
|
||||
hnsEndpoints[ep1v4],
|
||||
},
|
||||
resolverLAs: []string{gw1v4},
|
||||
expIPToExtDNS: map[netip.Addr][maxExtDNS]extDNSEntry{
|
||||
netip.MustParseAddr(ep1v4): {{IPStr: dns1v4}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "limit of three dns servers",
|
||||
epToAdd: &EndpointInterface{
|
||||
addr: makeIPNet(epFiveDNS, "32"),
|
||||
},
|
||||
hnsEndpoints: []hcsshim.HNSEndpoint{
|
||||
hnsEndpoints[epFiveDNS],
|
||||
},
|
||||
resolverLAs: []string{gw1v4},
|
||||
// Expect the internal resolver to keep the first three ext-servers.
|
||||
expIPToExtDNS: map[netip.Addr][maxExtDNS]extDNSEntry{
|
||||
netip.MustParseAddr(epFiveDNS): {
|
||||
{IPStr: dns1v4},
|
||||
{IPStr: dns2v4},
|
||||
{IPStr: dns3v4},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "disabled internal resolver",
|
||||
epToAdd: &EndpointInterface{
|
||||
addr: makeIPNet(epNoIntDNS, "32"),
|
||||
},
|
||||
hnsEndpoints: []hcsshim.HNSEndpoint{
|
||||
hnsEndpoints[epNoIntDNS],
|
||||
hnsEndpoints[ep2v4],
|
||||
},
|
||||
resolverLAs: []string{gw1v4},
|
||||
},
|
||||
{
|
||||
name: "missing internal resolver",
|
||||
epToAdd: &EndpointInterface{
|
||||
addr: makeIPNet(ep1v4, "32"),
|
||||
},
|
||||
hnsEndpoints: []hcsshim.HNSEndpoint{
|
||||
hnsEndpoints[ep1v4],
|
||||
},
|
||||
// The only resolver is for the gateway on a different network.
|
||||
resolverLAs: []string{gw2v4},
|
||||
},
|
||||
{
|
||||
name: "multiple resolvers and endpoints",
|
||||
epToAdd: &EndpointInterface{
|
||||
addr: makeIPNet(ep2v4, "32"),
|
||||
},
|
||||
hnsEndpoints: []hcsshim.HNSEndpoint{
|
||||
hnsEndpoints[ep1v4],
|
||||
hnsEndpoints[ep2v4],
|
||||
},
|
||||
// Put the internal resolver for this network second in the list.
|
||||
expResolverIdx: 1,
|
||||
resolverLAs: []string{gw2v4, gw1v4},
|
||||
expIPToExtDNS: map[netip.Addr][maxExtDNS]extDNSEntry{
|
||||
netip.MustParseAddr(ep2v4): {{IPStr: dns2v4}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ipv6",
|
||||
epToAdd: &EndpointInterface{
|
||||
addrv6: makeIPNet(ep1v6, "80"),
|
||||
},
|
||||
hnsEndpoints: []hcsshim.HNSEndpoint{
|
||||
hnsEndpoints[ep1v6],
|
||||
},
|
||||
resolverLAs: []string{gw1v6},
|
||||
expIPToExtDNS: map[netip.Addr][maxExtDNS]extDNSEntry{
|
||||
netip.MustParseAddr(ep1v6): {{IPStr: dns1v4}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
eMapCmpOpts := []cmp.Option{
|
||||
cmpopts.EquateEmpty(),
|
||||
cmpopts.EquateComparable(netip.Addr{}),
|
||||
cmpopts.IgnoreUnexported(extDNSEntry{}),
|
||||
}
|
||||
emptyEMap := map[netip.Addr][maxExtDNS]extDNSEntry{}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Set up resolvers with the required listen-addresses.
|
||||
var resolvers []*Resolver
|
||||
for _, la := range tc.resolverLAs {
|
||||
resolvers = append(resolvers, NewResolver(la, true, nil))
|
||||
}
|
||||
|
||||
// Add the endpoint and check expected results.
|
||||
err := addEpToResolverImpl(context.TODO(),
|
||||
"netname", "epname", tc.epToAdd, resolvers, tc.hnsEndpoints)
|
||||
assert.Check(t, err)
|
||||
for i, resolver := range resolvers {
|
||||
if i == tc.expResolverIdx {
|
||||
assert.Check(t, is.DeepEqual(resolver.ipToExtDNS.eMap, tc.expIPToExtDNS,
|
||||
eMapCmpOpts...), fmt.Sprintf("resolveridx=%d", i))
|
||||
} else {
|
||||
assert.Check(t, is.DeepEqual(resolver.ipToExtDNS.eMap, emptyEMap,
|
||||
eMapCmpOpts...), fmt.Sprintf("resolveridx=%d", i))
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the endpoint, check nothing got left behind.
|
||||
err = deleteEpFromResolverImpl("epname", tc.epToAdd, resolvers, tc.hnsEndpoints)
|
||||
assert.Check(t, err)
|
||||
for i, resolver := range resolvers {
|
||||
assert.Check(t, is.DeepEqual(resolver.ipToExtDNS.eMap, emptyEMap,
|
||||
eMapCmpOpts...), fmt.Sprintf("resolveridx=%d", i))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -646,17 +646,47 @@ func setIPv6(nspath, iface string, enable bool) error {
|
||||
value = '0'
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
if curVal, err := os.ReadFile(path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
log.G(context.TODO()).WithError(err).Warn("Cannot configure IPv6 forwarding on container interface. Has IPv6 been disabled in this node's kernel?")
|
||||
if enable {
|
||||
log.G(context.TODO()).WithError(err).Warn("Cannot enable IPv6 on container interface. Has IPv6 been disabled in this node's kernel?")
|
||||
} else {
|
||||
log.G(context.TODO()).WithError(err).Debug("Not disabling IPv6 on container interface. Has IPv6 been disabled in this node's kernel?")
|
||||
}
|
||||
return
|
||||
}
|
||||
errCh <- err
|
||||
return
|
||||
} else if len(curVal) > 0 && curVal[0] == value {
|
||||
// Nothing to do, the setting is already correct.
|
||||
return
|
||||
}
|
||||
|
||||
if err = os.WriteFile(path, []byte{value, '\n'}, 0o644); err != nil {
|
||||
errCh <- fmt.Errorf("failed to %s IPv6 forwarding for container's interface %s: %w", action, iface, err)
|
||||
if err = os.WriteFile(path, []byte{value, '\n'}, 0o644); err != nil || os.Getenv("DOCKER_TEST_RO_DISABLE_IPV6") != "" {
|
||||
logger := log.G(context.TODO()).WithFields(log.Fields{
|
||||
"error": err,
|
||||
"interface": iface,
|
||||
})
|
||||
if enable {
|
||||
// The user asked for IPv6 on the interface, and we can't give it to them.
|
||||
// But, in line with the IsNotExist case above, just log.
|
||||
logger.Warn("Cannot enable IPv6 on container interface, continuing.")
|
||||
} else if os.Getenv("DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE") == "1" {
|
||||
// TODO(robmry) - remove this escape hatch for https://github.com/moby/moby/issues/47751
|
||||
// If the "/proc" file exists but isn't writable, we can't disable IPv6, which is
|
||||
// https://github.com/moby/moby/security/advisories/GHSA-x84c-p2g9-rqv9 ... so,
|
||||
// the user is required to override the error (or configure IPv6, or disable IPv6
|
||||
// by default in the OS, or make the "/proc" file writable). Once it's possible
|
||||
// to enable IPv6 without having to configure IPAM etc, the env var should be
|
||||
// removed. Then the user will have to explicitly enable IPv6 if it can't be
|
||||
// disabled on the interface.
|
||||
logger.Info("Cannot disable IPv6 on container interface but DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1, continuing.")
|
||||
} else {
|
||||
logger.Error("Cannot disable IPv6 on container interface. Set env var DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1 to ignore.")
|
||||
errCh <- fmt.Errorf(
|
||||
"failed to %s IPv6 on container's interface %s, set env var DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1 to ignore this error",
|
||||
action, iface)
|
||||
}
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/docker/docker/libnetwork/internal/netiputil"
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/miekg/dns"
|
||||
"go.opentelemetry.io/otel"
|
||||
@@ -65,17 +67,25 @@ type extDNSEntry struct {
|
||||
HostLoopback bool
|
||||
}
|
||||
|
||||
func (e extDNSEntry) String() string {
|
||||
if e.HostLoopback {
|
||||
return "host(" + e.IPStr + ")"
|
||||
}
|
||||
return e.IPStr
|
||||
}
|
||||
|
||||
// Resolver is the embedded DNS server in Docker. It operates by listening on
|
||||
// the container's loopback interface for DNS queries.
|
||||
type Resolver struct {
|
||||
backend DNSBackend
|
||||
extDNSList [maxExtDNS]extDNSEntry
|
||||
extDNSList [maxExtDNS]extDNSEntry // Ext servers to use when there's no entry in ipToExtDNS.
|
||||
ipToExtDNS addrToExtDNSMap // DNS query source IP -> ext servers.
|
||||
server *dns.Server
|
||||
conn *net.UDPConn
|
||||
tcpServer *dns.Server
|
||||
tcpListen *net.TCPListener
|
||||
err error
|
||||
listenAddress string
|
||||
listenAddress netip.Addr
|
||||
proxyDNS atomic.Bool
|
||||
startCh chan struct{}
|
||||
logger *log.Entry
|
||||
@@ -87,18 +97,45 @@ type Resolver struct {
|
||||
// NewResolver creates a new instance of the Resolver
|
||||
func NewResolver(address string, proxyDNS bool, backend DNSBackend) *Resolver {
|
||||
r := &Resolver{
|
||||
backend: backend,
|
||||
listenAddress: address,
|
||||
err: fmt.Errorf("setup not done yet"),
|
||||
startCh: make(chan struct{}, 1),
|
||||
fwdSem: semaphore.NewWeighted(maxConcurrent),
|
||||
logInverval: rate.Sometimes{Interval: logInterval},
|
||||
backend: backend,
|
||||
err: fmt.Errorf("setup not done yet"),
|
||||
startCh: make(chan struct{}, 1),
|
||||
fwdSem: semaphore.NewWeighted(maxConcurrent),
|
||||
logInverval: rate.Sometimes{Interval: logInterval},
|
||||
}
|
||||
r.listenAddress, _ = netip.ParseAddr(address)
|
||||
r.proxyDNS.Store(proxyDNS)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
type addrToExtDNSMap struct {
|
||||
mu sync.Mutex
|
||||
eMap map[netip.Addr][maxExtDNS]extDNSEntry
|
||||
}
|
||||
|
||||
func (am *addrToExtDNSMap) get(addr netip.Addr) ([maxExtDNS]extDNSEntry, bool) {
|
||||
am.mu.Lock()
|
||||
defer am.mu.Unlock()
|
||||
entries, ok := am.eMap[addr]
|
||||
return entries, ok
|
||||
}
|
||||
|
||||
func (am *addrToExtDNSMap) set(addr netip.Addr, entries []extDNSEntry) {
|
||||
var e [maxExtDNS]extDNSEntry
|
||||
copy(e[:], entries)
|
||||
am.mu.Lock()
|
||||
defer am.mu.Unlock()
|
||||
if len(entries) > 0 {
|
||||
if am.eMap == nil {
|
||||
am.eMap = map[netip.Addr][maxExtDNS]extDNSEntry{}
|
||||
}
|
||||
am.eMap[addr] = e
|
||||
} else {
|
||||
delete(am.eMap, addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resolver) log(ctx context.Context) *log.Entry {
|
||||
if r.logger == nil {
|
||||
return log.G(ctx)
|
||||
@@ -108,25 +145,23 @@ func (r *Resolver) log(ctx context.Context) *log.Entry {
|
||||
|
||||
// SetupFunc returns the setup function that should be run in the container's
|
||||
// network namespace.
|
||||
func (r *Resolver) SetupFunc(port int) func() {
|
||||
func (r *Resolver) SetupFunc(port uint16) func() {
|
||||
return func() {
|
||||
var err error
|
||||
|
||||
// DNS operates primarily on UDP
|
||||
r.conn, err = net.ListenUDP("udp", &net.UDPAddr{
|
||||
IP: net.ParseIP(r.listenAddress),
|
||||
Port: port,
|
||||
})
|
||||
r.conn, err = net.ListenUDP("udp", net.UDPAddrFromAddrPort(
|
||||
netip.AddrPortFrom(r.listenAddress, port)),
|
||||
)
|
||||
if err != nil {
|
||||
r.err = fmt.Errorf("error in opening name server socket %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Listen on a TCP as well
|
||||
r.tcpListen, err = net.ListenTCP("tcp", &net.TCPAddr{
|
||||
IP: net.ParseIP(r.listenAddress),
|
||||
Port: port,
|
||||
})
|
||||
r.tcpListen, err = net.ListenTCP("tcp", net.TCPAddrFromAddrPort(
|
||||
netip.AddrPortFrom(r.listenAddress, port)),
|
||||
)
|
||||
if err != nil {
|
||||
r.err = fmt.Errorf("error in opening name TCP server socket %v", err)
|
||||
return
|
||||
@@ -186,15 +221,10 @@ func (r *Resolver) Stop() {
|
||||
}
|
||||
|
||||
// SetExtServers configures the external nameservers the resolver should use
|
||||
// when forwarding queries.
|
||||
// when forwarding queries, unless SetExtServersForSrc has configured servers
|
||||
// for the DNS client making the request.
|
||||
func (r *Resolver) SetExtServers(extDNS []extDNSEntry) {
|
||||
l := len(extDNS)
|
||||
if l > maxExtDNS {
|
||||
l = maxExtDNS
|
||||
}
|
||||
for i := 0; i < l; i++ {
|
||||
r.extDNSList[i] = extDNS[i]
|
||||
}
|
||||
copy(r.extDNSList[:], r.filterExtServers(extDNS))
|
||||
}
|
||||
|
||||
// SetForwardingPolicy re-configures the embedded DNS resolver to either enable or disable forwarding DNS queries to
|
||||
@@ -203,8 +233,17 @@ func (r *Resolver) SetForwardingPolicy(policy bool) {
|
||||
r.proxyDNS.Store(policy)
|
||||
}
|
||||
|
||||
// SetExtServersForSrc configures the external nameservers the resolver should
|
||||
// use when forwarding queries from srcAddr. If set, these servers will be used
|
||||
// in preference to servers set by SetExtServers. Supplying a nil or empty extDNS
|
||||
// deletes nameservers for srcAddr.
|
||||
func (r *Resolver) SetExtServersForSrc(srcAddr netip.Addr, extDNS []extDNSEntry) error {
|
||||
r.ipToExtDNS.set(srcAddr, r.filterExtServers(extDNS))
|
||||
return nil
|
||||
}
|
||||
|
||||
// NameServer returns the IP of the DNS resolver for the containers.
|
||||
func (r *Resolver) NameServer() string {
|
||||
func (r *Resolver) NameServer() netip.Addr {
|
||||
return r.listenAddress
|
||||
}
|
||||
|
||||
@@ -213,6 +252,23 @@ func (r *Resolver) ResolverOptions() []string {
|
||||
return []string{"ndots:0"}
|
||||
}
|
||||
|
||||
// filterExtServers removes the resolver's own address from extDNS if present,
|
||||
// and returns the result.
|
||||
func (r *Resolver) filterExtServers(extDNS []extDNSEntry) []extDNSEntry {
|
||||
result := make([]extDNSEntry, 0, len(extDNS))
|
||||
for _, e := range extDNS {
|
||||
if !e.HostLoopback {
|
||||
if ra, _ := netip.ParseAddr(e.IPStr); ra == r.listenAddress {
|
||||
log.G(context.TODO()).Infof("[resolver] not using own address (%s) as an external DNS server",
|
||||
r.listenAddress)
|
||||
continue
|
||||
}
|
||||
}
|
||||
result = append(result, e)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
//nolint:gosec // The RNG is not used in a security-sensitive context.
|
||||
var (
|
||||
shuffleRNG = rand.New(rand.NewSource(time.Now().Unix()))
|
||||
@@ -430,17 +486,15 @@ func (r *Resolver) serveDNS(w dns.ResponseWriter, query *dns.Msg) {
|
||||
return
|
||||
}
|
||||
|
||||
if r.proxyDNS.Load() {
|
||||
// If the user sets ndots > 0 explicitly and the query is
|
||||
// in the root domain don't forward it out. We will return
|
||||
// failure and let the client retry with the search domain
|
||||
// attached.
|
||||
if (queryType == dns.TypeA || queryType == dns.TypeAAAA) && r.backend.NdotsSet() &&
|
||||
!strings.Contains(strings.TrimSuffix(queryName, "."), ".") {
|
||||
resp = createRespMsg(query)
|
||||
} else {
|
||||
resp = r.forwardExtDNS(ctx, w.LocalAddr().Network(), query)
|
||||
}
|
||||
// If the user sets ndots > 0 explicitly and the query is
|
||||
// in the root domain don't forward it out. We will return
|
||||
// failure and let the client retry with the search domain
|
||||
// attached.
|
||||
if (queryType == dns.TypeA || queryType == dns.TypeAAAA) && r.backend.NdotsSet() &&
|
||||
!strings.Contains(strings.TrimSuffix(queryName, "."), ".") {
|
||||
resp = createRespMsg(query)
|
||||
} else {
|
||||
resp = r.forwardExtDNS(ctx, w.LocalAddr().Network(), w.RemoteAddr(), query)
|
||||
}
|
||||
|
||||
if resp == nil {
|
||||
@@ -481,14 +535,22 @@ func (r *Resolver) dialExtDNS(proto string, server extDNSEntry) (net.Conn, error
|
||||
return extConn, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) forwardExtDNS(ctx context.Context, proto string, query *dns.Msg) *dns.Msg {
|
||||
func (r *Resolver) forwardExtDNS(ctx context.Context, proto string, remoteAddr net.Addr, query *dns.Msg) *dns.Msg {
|
||||
ctx, span := otel.Tracer("").Start(ctx, "resolver.forwardExtDNS")
|
||||
defer span.End()
|
||||
|
||||
for _, extDNS := range r.extDNSList {
|
||||
proxyDNS := r.proxyDNS.Load()
|
||||
for _, extDNS := range r.extDNS(netiputil.AddrPortFromNet(remoteAddr)) {
|
||||
if extDNS.IPStr == "" {
|
||||
break
|
||||
}
|
||||
// If proxyDNS is false, do not forward the request from the host's namespace
|
||||
// (don't access an external DNS server from an internal network). But, it is
|
||||
// safe to make the request from the container's network namespace - it'll fail
|
||||
// if the DNS server is not accessible, but the server may be on-net.
|
||||
if !proxyDNS && extDNS.HostLoopback {
|
||||
continue
|
||||
}
|
||||
|
||||
// limits the number of outstanding concurrent queries.
|
||||
ctx, cancel := context.WithTimeout(ctx, extIOTimeout)
|
||||
@@ -548,6 +610,13 @@ func (r *Resolver) forwardExtDNS(ctx context.Context, proto string, query *dns.M
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resolver) extDNS(remoteAddr netip.AddrPort) []extDNSEntry {
|
||||
if res, ok := r.ipToExtDNS.get(remoteAddr.Addr()); ok {
|
||||
return res[:]
|
||||
}
|
||||
return r.extDNSList[:]
|
||||
}
|
||||
|
||||
func (r *Resolver) exchange(ctx context.Context, proto string, extDNS extDNSEntry, query *dns.Msg) *dns.Msg {
|
||||
ctx, span := otel.Tracer("").Start(ctx, "resolver.exchange", trace.WithAttributes(
|
||||
attribute.String("libnet.resolver.upstream.proto", proto),
|
||||
|
||||
@@ -92,6 +92,7 @@ type resolvConfPathConfig struct {
|
||||
}
|
||||
|
||||
type containerConfig struct {
|
||||
containerConfigOS //nolint:nolintlint,unused // only populated on windows
|
||||
hostsPathConfig
|
||||
resolvConfPathConfig
|
||||
generic map[string]interface{}
|
||||
|
||||
@@ -4,6 +4,7 @@ package libnetwork
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/netip"
|
||||
"os"
|
||||
@@ -343,9 +344,9 @@ func (sb *Sandbox) rebuildDNS() error {
|
||||
}
|
||||
}
|
||||
|
||||
intNS, err := netip.ParseAddr(sb.resolver.NameServer())
|
||||
if err != nil {
|
||||
return err
|
||||
intNS := sb.resolver.NameServer()
|
||||
if !intNS.IsValid() {
|
||||
return fmt.Errorf("no listen-address for internal resolver")
|
||||
}
|
||||
|
||||
// Work out whether ndots has been set from host config or overrides.
|
||||
|
||||
@@ -12,6 +12,9 @@ import (
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// Linux-specific container configuration flags.
|
||||
type containerConfigOS struct{} //nolint:nolintlint,unused // only populated on windows
|
||||
|
||||
func releaseOSSboxResources(ns *osl.Namespace, ep *Endpoint) {
|
||||
for _, i := range ns.Interfaces() {
|
||||
// Only remove the interfaces owned by this endpoint from the sandbox.
|
||||
|
||||
7
libnetwork/sandbox_options_windows.go
Normal file
7
libnetwork/sandbox_options_windows.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package libnetwork
|
||||
|
||||
func OptionDNSNoProxy() SandboxOption {
|
||||
return func(sb *Sandbox) {
|
||||
sb.config.dnsNoProxy = true
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
//go:build !linux
|
||||
|
||||
package libnetwork
|
||||
|
||||
import "github.com/docker/docker/libnetwork/osl"
|
||||
|
||||
// Windows-specific container configuration flags.
|
||||
type containerConfigOS struct {
|
||||
dnsNoProxy bool
|
||||
}
|
||||
|
||||
func releaseOSSboxResources(*osl.Namespace, *Endpoint) {}
|
||||
|
||||
func (sb *Sandbox) updateGateway(*Endpoint) error {
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/log"
|
||||
@@ -55,23 +53,10 @@ type Ctx struct {
|
||||
authReq *Request
|
||||
}
|
||||
|
||||
func isChunked(r *http.Request) bool {
|
||||
// RFC 7230 specifies that content length is to be ignored if Transfer-Encoding is chunked
|
||||
if strings.EqualFold(r.Header.Get("Transfer-Encoding"), "chunked") {
|
||||
return true
|
||||
}
|
||||
for _, v := range r.TransferEncoding {
|
||||
if strings.EqualFold(v, "chunked") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AuthZRequest authorized the request to the docker daemon using authZ plugins
|
||||
func (ctx *Ctx) AuthZRequest(w http.ResponseWriter, r *http.Request) error {
|
||||
var body []byte
|
||||
if sendBody(ctx.requestURI, r.Header) && (r.ContentLength > 0 || isChunked(r)) && r.ContentLength < maxBodySize {
|
||||
if sendBody(ctx.requestURI, r.Header) && r.ContentLength > 0 && r.ContentLength < maxBodySize {
|
||||
var err error
|
||||
body, r.Body, err = drainBody(r.Body)
|
||||
if err != nil {
|
||||
@@ -124,6 +109,7 @@ func (ctx *Ctx) AuthZResponse(rm ResponseModifier, r *http.Request) error {
|
||||
if sendBody(ctx.requestURI, rm.Header()) {
|
||||
ctx.authReq.ResponseBody = rm.RawBody()
|
||||
}
|
||||
|
||||
for _, plugin := range ctx.plugins {
|
||||
log.G(context.TODO()).Debugf("AuthZ response using plugin %s", plugin.Name())
|
||||
|
||||
@@ -161,26 +147,10 @@ func drainBody(body io.ReadCloser) ([]byte, io.ReadCloser, error) {
|
||||
return nil, newBody, err
|
||||
}
|
||||
|
||||
func isAuthEndpoint(urlPath string) (bool, error) {
|
||||
// eg www.test.com/v1.24/auth/optional?optional1=something&optional2=something (version optional)
|
||||
matched, err := regexp.MatchString(`^[^\/]*\/(v\d[\d\.]*\/)?auth.*`, urlPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return matched, nil
|
||||
}
|
||||
|
||||
// sendBody returns true when request/response body should be sent to AuthZPlugin
|
||||
func sendBody(inURL string, header http.Header) bool {
|
||||
u, err := url.Parse(inURL)
|
||||
// Assume no if the URL cannot be parsed - an empty request will still be forwarded to the plugin and should be rejected
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
func sendBody(url string, header http.Header) bool {
|
||||
// Skip body for auth endpoint
|
||||
isAuth, err := isAuthEndpoint(u.Path)
|
||||
if isAuth || err != nil {
|
||||
if strings.HasSuffix(url, "/auth") {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -174,8 +174,8 @@ func TestDrainBody(t *testing.T) {
|
||||
|
||||
func TestSendBody(t *testing.T) {
|
||||
var (
|
||||
url = "nothing.com"
|
||||
testcases = []struct {
|
||||
url string
|
||||
contentType string
|
||||
expected bool
|
||||
}{
|
||||
@@ -219,93 +219,15 @@ func TestSendBody(t *testing.T) {
|
||||
contentType: "",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/auth",
|
||||
contentType: "",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/auth?p1=test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/test?p1=/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/something/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/auth/test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/v1.24/auth/test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "nothing.com/v1/auth/test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "www.nothing.com/v1.24/auth/test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "https://www.nothing.com/v1.24/auth/test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "http://nothing.com/v1.24/auth/test",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
url: "www.nothing.com/test?p1=/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: "http://www.nothing.com/test?p1=/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: "www.nothing.com/something/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
url: "https://www.nothing.com/something/auth",
|
||||
contentType: "application/json;charset=UTF8",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
for _, testcase := range testcases {
|
||||
header := http.Header{}
|
||||
header.Set("Content-Type", testcase.contentType)
|
||||
if testcase.url == "" {
|
||||
testcase.url = "nothing.com"
|
||||
}
|
||||
|
||||
if b := sendBody(testcase.url, header); b != testcase.expected {
|
||||
t.Fatalf("sendBody failed: url: %s, content-type: %s; Expected: %t, Actual: %t", testcase.url, testcase.contentType, testcase.expected, b)
|
||||
if b := sendBody(url, header); b != testcase.expected {
|
||||
t.Fatalf("Unexpected Content-Type; Expected: %t, Actual: %t", testcase.expected, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
pkg/system/stat_illumos.go
Normal file
15
pkg/system/stat_illumos.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package system // import "github.com/docker/docker/pkg/system"
|
||||
|
||||
import "syscall"
|
||||
|
||||
// fromStatT converts a syscall.Stat_t type to a system.Stat_t type
|
||||
func fromStatT(s *syscall.Stat_t) (*StatT, error) {
|
||||
return &StatT{
|
||||
size: s.Size,
|
||||
mode: uint32(s.Mode),
|
||||
uid: s.Uid,
|
||||
gid: s.Gid,
|
||||
rdev: uint64(s.Rdev),
|
||||
mtim: s.Mtim,
|
||||
}, nil
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func (p *profileData) generateDefault(out io.Writer) error {
|
||||
return compiled.Execute(out, p)
|
||||
}
|
||||
|
||||
// macrosExists checks if the passed macro exists.
|
||||
// macroExists checks if the passed macro exists.
|
||||
func macroExists(m string) bool {
|
||||
_, err := os.Stat(path.Join(profileDirectory, m))
|
||||
return err == nil
|
||||
|
||||
@@ -25,6 +25,10 @@ profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
|
||||
umount,
|
||||
# Host (privileged) processes may send signals to container processes.
|
||||
signal (receive) peer=unconfined,
|
||||
# runc may send signals to container processes (for "docker stop").
|
||||
signal (receive) peer=runc,
|
||||
# crun may send signals to container processes (for "docker stop" when used with crun OCI runtime).
|
||||
signal (receive) peer=crun,
|
||||
# dockerd may send signals to container processes (for "docker kill").
|
||||
signal (receive) peer={{.DaemonProfile}},
|
||||
# Container processes may send signals amongst themselves.
|
||||
|
||||
19
vendor.mod
19
vendor.mod
@@ -25,7 +25,7 @@ require (
|
||||
github.com/aws/smithy-go v1.19.0
|
||||
github.com/cloudflare/cfssl v1.6.4
|
||||
github.com/containerd/cgroups/v3 v3.0.3
|
||||
github.com/containerd/containerd v1.7.15
|
||||
github.com/containerd/containerd v1.7.16
|
||||
github.com/containerd/continuity v0.4.3
|
||||
github.com/containerd/fifo v1.1.0
|
||||
github.com/containerd/log v0.1.0
|
||||
@@ -60,13 +60,13 @@ require (
|
||||
github.com/miekg/dns v1.1.57
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.1
|
||||
github.com/mitchellh/copystructure v1.2.0
|
||||
github.com/moby/buildkit v0.13.1
|
||||
github.com/moby/buildkit v0.13.2
|
||||
github.com/moby/docker-image-spec v1.3.1
|
||||
github.com/moby/ipvs v1.1.0
|
||||
github.com/moby/locker v1.0.1
|
||||
github.com/moby/patternmatcher v0.6.0
|
||||
github.com/moby/pubsub v1.0.0
|
||||
github.com/moby/swarmkit/v2 v2.0.0-20240125134710-dcda100a8261
|
||||
github.com/moby/swarmkit/v2 v2.0.0-20240412154004-f3ffc0881d0e
|
||||
github.com/moby/sys/mount v0.3.3
|
||||
github.com/moby/sys/mountinfo v0.7.1
|
||||
github.com/moby/sys/sequential v0.5.0
|
||||
@@ -78,7 +78,7 @@ require (
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.0-rc5
|
||||
github.com/opencontainers/runc v1.1.12
|
||||
github.com/opencontainers/runtime-spec v1.1.0
|
||||
github.com/opencontainers/runtime-spec v1.2.0
|
||||
github.com/opencontainers/selinux v1.11.0
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/pkg/errors v0.9.1
|
||||
@@ -87,7 +87,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/tonistiigi/fsutil v0.0.0-20240301111122-7525a1af2bb5
|
||||
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c
|
||||
github.com/tonistiigi/go-archvariant v1.0.0
|
||||
github.com/vbatts/tar-split v0.11.5
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2
|
||||
@@ -110,7 +110,7 @@ require (
|
||||
google.golang.org/protobuf v1.33.0
|
||||
gotest.tools/v3 v3.5.1
|
||||
resenje.org/singleflight v0.4.1
|
||||
tags.cncf.io/container-device-interface v0.6.2
|
||||
tags.cncf.io/container-device-interface v0.7.2
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -132,6 +132,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
@@ -147,6 +148,7 @@ require (
|
||||
github.com/containernetworking/cni v1.1.2 // indirect
|
||||
github.com/containernetworking/plugins v1.4.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
@@ -179,6 +181,7 @@ require (
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect
|
||||
github.com/package-url/packageurl-go v0.1.1-0.20220428063043-89078438f170 // indirect
|
||||
github.com/philhofer/fwd v1.1.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
@@ -186,6 +189,7 @@ require (
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
github.com/spdx/tools-golang v0.5.3 // indirect
|
||||
github.com/stretchr/testify v1.8.4 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
github.com/tinylib/msgp v1.1.8 // indirect
|
||||
github.com/tonistiigi/go-actions-cache v0.0.0-20240227172821-a0b64f338598 // indirect
|
||||
@@ -221,7 +225,8 @@ require (
|
||||
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/klog/v2 v2.90.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
tags.cncf.io/container-device-interface/specs-go v0.6.0 // indirect
|
||||
tags.cncf.io/container-device-interface/specs-go v0.7.0 // indirect
|
||||
)
|
||||
|
||||
32
vendor.sum
32
vendor.sum
@@ -103,6 +103,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
|
||||
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
@@ -154,8 +156,8 @@ github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGD
|
||||
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
|
||||
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
|
||||
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/containerd/containerd v1.7.15 h1:afEHXdil9iAm03BmhjzKyXnnEBtjaLJefdU7DV0IFes=
|
||||
github.com/containerd/containerd v1.7.15/go.mod h1:ISzRRTMF8EXNpJlTzyr2XMhN+j9K302C21/+cr3kUnY=
|
||||
github.com/containerd/containerd v1.7.16 h1:7Zsfe8Fkj4Wi2My6DXGQ87hiqIrmOXolm72ZEkFU5Mg=
|
||||
github.com/containerd/containerd v1.7.16/go.mod h1:NL49g7A/Fui7ccmxV6zkBWwqMgmMxFWzujYCc+JLt7k=
|
||||
github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
|
||||
github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
|
||||
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
|
||||
@@ -476,8 +478,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs=
|
||||
github.com/moby/buildkit v0.13.1 h1:L8afOFhPq2RPJJSr/VyzbufwID7jquZVB7oFHbPRcPE=
|
||||
github.com/moby/buildkit v0.13.1/go.mod h1:aNmNQKLBFYAOFuzQjR3VA27/FijlvtBD1pjNwTSN37k=
|
||||
github.com/moby/buildkit v0.13.2 h1:nXNszM4qD9E7QtG7bFWPnDI1teUQFQglBzon/IU3SzI=
|
||||
github.com/moby/buildkit v0.13.2/go.mod h1:2cyVOv9NoHM7arphK9ZfHIWKn9YVZRFd1wXB8kKmEzY=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/ipvs v1.1.0 h1:ONN4pGaZQgAx+1Scz5RvWV4Q7Gb+mvfRh3NsPS+1XQQ=
|
||||
@@ -488,8 +490,8 @@ github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkV
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/pubsub v1.0.0 h1:jkp/imWsmJz2f6LyFsk7EkVeN2HxR/HTTOY8kHrsxfA=
|
||||
github.com/moby/pubsub v1.0.0/go.mod h1:bXSO+3h5MNXXCaEG+6/NlAIk7MMZbySZlnB+cUQhKKc=
|
||||
github.com/moby/swarmkit/v2 v2.0.0-20240125134710-dcda100a8261 h1:mjLf2jYrqtIS4LvLzg0gNyJR4rMXS4X5Bg1A4hOhVMs=
|
||||
github.com/moby/swarmkit/v2 v2.0.0-20240125134710-dcda100a8261/go.mod h1:oRJU1d0hrkkwCtouwfQGcIAKcVEkclMYoLWocqrg6gI=
|
||||
github.com/moby/swarmkit/v2 v2.0.0-20240412154004-f3ffc0881d0e h1:ZCx3cYIxF6OdZkiSbesvyJyJetMmICNvMGVHFop+Mec=
|
||||
github.com/moby/swarmkit/v2 v2.0.0-20240412154004-f3ffc0881d0e/go.mod h1:kNy225f/gWAnF8wPftteMc5nbAHhrH+HUfvyjmhFjeQ=
|
||||
github.com/moby/sys/mount v0.3.3 h1:fX1SVkXFJ47XWDoeFW4Sq7PdQJnV2QIDZAqjNqgEjUs=
|
||||
github.com/moby/sys/mount v0.3.3/go.mod h1:PBaEorSNTLG5t/+4EgukEQVlAvVEc6ZjTySwKdqp5K0=
|
||||
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
|
||||
@@ -540,8 +542,8 @@ github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVn
|
||||
github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss=
|
||||
github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8=
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg=
|
||||
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
||||
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0=
|
||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI=
|
||||
github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||
@@ -665,12 +667,14 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/tedsuo/ifrit v0.0.0-20230330192023-5cba443a66c4 h1:MGZzzxBuPuK4J0XQo+0uy0NnXQGKzHXhYp5oG1Wy860=
|
||||
github.com/tedsuo/ifrit v0.0.0-20230330192023-5cba443a66c4/go.mod h1:eyZnKCc955uh98WQvzOm0dgAeLnf2O0Rz0LPoC5ze+0=
|
||||
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
|
||||
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20240301111122-7525a1af2bb5 h1:oZS8KCqAg62sxJkEq/Ppzqrb6EooqzWtL8Oaex7bc5c=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20240301111122-7525a1af2bb5/go.mod h1:vbbYqJlnswsbJqWUcJN8fKtBhnEgldDrcagTgnBVKKM=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c h1:+6wg/4ORAbnSoGDzg2Q1i3CeMcT/jjhye/ZfnBHy7/M=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c/go.mod h1:vbbYqJlnswsbJqWUcJN8fKtBhnEgldDrcagTgnBVKKM=
|
||||
github.com/tonistiigi/go-actions-cache v0.0.0-20240227172821-a0b64f338598 h1:DA/NDC0YbMdnfcOSUzAnbUZE6dSM54d+0hrBqG+bOfs=
|
||||
github.com/tonistiigi/go-actions-cache v0.0.0-20240227172821-a0b64f338598/go.mod h1:anhKd3mnC1shAbQj1Q4IJ+w6xqezxnyDYlx/yKa7IXM=
|
||||
github.com/tonistiigi/go-archvariant v1.0.0 h1:5LC1eDWiBNflnTF1prCiX09yfNHIxDC/aukdhCdTyb0=
|
||||
@@ -1102,7 +1106,7 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
tags.cncf.io/container-device-interface v0.6.2 h1:dThE6dtp/93ZDGhqaED2Pu374SOeUkBfuvkLuiTdwzg=
|
||||
tags.cncf.io/container-device-interface v0.6.2/go.mod h1:Shusyhjs1A5Na/kqPVLL0KqnHQHuunol9LFeUNkuGVE=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.6.0 h1:V+tJJN6dqu8Vym6p+Ru+K5mJ49WL6Aoc5SJFSY0RLsQ=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.6.0/go.mod h1:hMAwAbMZyBLdmYqWgYcKH0F/yctNpV3P35f+/088A80=
|
||||
tags.cncf.io/container-device-interface v0.7.2 h1:MLqGnWfOr1wB7m08ieI4YJ3IoLKKozEnnNYBtacDPQU=
|
||||
tags.cncf.io/container-device-interface v0.7.2/go.mod h1:Xb1PvXv2BhfNb3tla4r9JL129ck1Lxv9KuU6eVOfKto=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.7.0 h1:w/maMGVeLP6TIQJVYT5pbqTi8SCw/iHZ+n4ignuGHqg=
|
||||
tags.cncf.io/container-device-interface/specs-go v0.7.0/go.mod h1:hMAwAbMZyBLdmYqWgYcKH0F/yctNpV3P35f+/088A80=
|
||||
|
||||
139
vendor/code.cloudfoundry.org/clock/fakeclock/fake_clock.go
generated
vendored
Normal file
139
vendor/code.cloudfoundry.org/clock/fakeclock/fake_clock.go
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
package fakeclock
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"code.cloudfoundry.org/clock"
|
||||
)
|
||||
|
||||
type timeWatcher interface {
|
||||
timeUpdated(time.Time)
|
||||
shouldFire(time.Time) bool
|
||||
repeatable() bool
|
||||
}
|
||||
|
||||
type FakeClock struct {
|
||||
now time.Time
|
||||
|
||||
watchers map[timeWatcher]struct{}
|
||||
cond *sync.Cond
|
||||
}
|
||||
|
||||
func NewFakeClock(now time.Time) *FakeClock {
|
||||
return &FakeClock{
|
||||
now: now,
|
||||
watchers: make(map[timeWatcher]struct{}),
|
||||
cond: &sync.Cond{L: &sync.Mutex{}},
|
||||
}
|
||||
}
|
||||
|
||||
func (clock *FakeClock) Since(t time.Time) time.Duration {
|
||||
return clock.Now().Sub(t)
|
||||
}
|
||||
|
||||
func (clock *FakeClock) Now() time.Time {
|
||||
clock.cond.L.Lock()
|
||||
defer clock.cond.L.Unlock()
|
||||
|
||||
return clock.now
|
||||
}
|
||||
|
||||
func (clock *FakeClock) Increment(duration time.Duration) {
|
||||
clock.increment(duration, false, 0)
|
||||
}
|
||||
|
||||
func (clock *FakeClock) IncrementBySeconds(seconds uint64) {
|
||||
clock.Increment(time.Duration(seconds) * time.Second)
|
||||
}
|
||||
|
||||
func (clock *FakeClock) WaitForWatcherAndIncrement(duration time.Duration) {
|
||||
clock.WaitForNWatchersAndIncrement(duration, 1)
|
||||
}
|
||||
|
||||
func (clock *FakeClock) WaitForNWatchersAndIncrement(duration time.Duration, numWatchers int) {
|
||||
clock.increment(duration, true, numWatchers)
|
||||
}
|
||||
|
||||
func (clock *FakeClock) NewTimer(d time.Duration) clock.Timer {
|
||||
timer := newFakeTimer(clock, d, false)
|
||||
clock.addTimeWatcher(timer)
|
||||
|
||||
return timer
|
||||
}
|
||||
|
||||
func (clock *FakeClock) Sleep(d time.Duration) {
|
||||
<-clock.NewTimer(d).C()
|
||||
}
|
||||
|
||||
func (clock *FakeClock) After(d time.Duration) <-chan time.Time {
|
||||
return clock.NewTimer(d).C()
|
||||
}
|
||||
|
||||
func (clock *FakeClock) NewTicker(d time.Duration) clock.Ticker {
|
||||
if d <= 0 {
|
||||
panic(errors.New("duration must be greater than zero"))
|
||||
}
|
||||
|
||||
timer := newFakeTimer(clock, d, true)
|
||||
clock.addTimeWatcher(timer)
|
||||
|
||||
return newFakeTicker(timer)
|
||||
}
|
||||
|
||||
func (clock *FakeClock) WatcherCount() int {
|
||||
clock.cond.L.Lock()
|
||||
defer clock.cond.L.Unlock()
|
||||
|
||||
return len(clock.watchers)
|
||||
}
|
||||
|
||||
func (clock *FakeClock) increment(duration time.Duration, waitForWatchers bool, numWatchers int) {
|
||||
clock.cond.L.Lock()
|
||||
|
||||
for waitForWatchers && len(clock.watchers) < numWatchers {
|
||||
clock.cond.Wait()
|
||||
}
|
||||
|
||||
now := clock.now.Add(duration)
|
||||
clock.now = now
|
||||
|
||||
watchers := make([]timeWatcher, 0)
|
||||
newWatchers := map[timeWatcher]struct{}{}
|
||||
for w, _ := range clock.watchers {
|
||||
fire := w.shouldFire(now)
|
||||
if fire {
|
||||
watchers = append(watchers, w)
|
||||
}
|
||||
|
||||
if !fire || w.repeatable() {
|
||||
newWatchers[w] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
clock.watchers = newWatchers
|
||||
|
||||
clock.cond.L.Unlock()
|
||||
|
||||
for _, w := range watchers {
|
||||
w.timeUpdated(now)
|
||||
}
|
||||
}
|
||||
|
||||
func (clock *FakeClock) addTimeWatcher(tw timeWatcher) {
|
||||
clock.cond.L.Lock()
|
||||
clock.watchers[tw] = struct{}{}
|
||||
clock.cond.L.Unlock()
|
||||
|
||||
// force the timer to fire
|
||||
clock.Increment(0)
|
||||
|
||||
clock.cond.Broadcast()
|
||||
}
|
||||
|
||||
func (clock *FakeClock) removeTimeWatcher(tw timeWatcher) {
|
||||
clock.cond.L.Lock()
|
||||
delete(clock.watchers, tw)
|
||||
clock.cond.L.Unlock()
|
||||
}
|
||||
25
vendor/code.cloudfoundry.org/clock/fakeclock/fake_ticker.go
generated
vendored
Normal file
25
vendor/code.cloudfoundry.org/clock/fakeclock/fake_ticker.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package fakeclock
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"code.cloudfoundry.org/clock"
|
||||
)
|
||||
|
||||
type fakeTicker struct {
|
||||
timer clock.Timer
|
||||
}
|
||||
|
||||
func newFakeTicker(timer *fakeTimer) *fakeTicker {
|
||||
return &fakeTicker{
|
||||
timer: timer,
|
||||
}
|
||||
}
|
||||
|
||||
func (ft *fakeTicker) C() <-chan time.Time {
|
||||
return ft.timer.C()
|
||||
}
|
||||
|
||||
func (ft *fakeTicker) Stop() {
|
||||
ft.timer.Stop()
|
||||
}
|
||||
87
vendor/code.cloudfoundry.org/clock/fakeclock/fake_timer.go
generated
vendored
Normal file
87
vendor/code.cloudfoundry.org/clock/fakeclock/fake_timer.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
package fakeclock
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type fakeTimer struct {
|
||||
clock *FakeClock
|
||||
|
||||
mutex sync.Mutex
|
||||
completionTime time.Time
|
||||
channel chan time.Time
|
||||
duration time.Duration
|
||||
repeat bool
|
||||
}
|
||||
|
||||
func newFakeTimer(clock *FakeClock, d time.Duration, repeat bool) *fakeTimer {
|
||||
return &fakeTimer{
|
||||
clock: clock,
|
||||
completionTime: clock.Now().Add(d),
|
||||
channel: make(chan time.Time, 1),
|
||||
duration: d,
|
||||
repeat: repeat,
|
||||
}
|
||||
}
|
||||
|
||||
func (ft *fakeTimer) C() <-chan time.Time {
|
||||
ft.mutex.Lock()
|
||||
defer ft.mutex.Unlock()
|
||||
return ft.channel
|
||||
}
|
||||
|
||||
func (ft *fakeTimer) reset(d time.Duration) bool {
|
||||
currentTime := ft.clock.Now()
|
||||
|
||||
ft.mutex.Lock()
|
||||
active := !ft.completionTime.IsZero()
|
||||
ft.completionTime = currentTime.Add(d)
|
||||
ft.mutex.Unlock()
|
||||
return active
|
||||
}
|
||||
|
||||
func (ft *fakeTimer) Reset(d time.Duration) bool {
|
||||
active := ft.reset(d)
|
||||
ft.clock.addTimeWatcher(ft)
|
||||
return active
|
||||
}
|
||||
|
||||
func (ft *fakeTimer) Stop() bool {
|
||||
ft.mutex.Lock()
|
||||
active := !ft.completionTime.IsZero()
|
||||
ft.mutex.Unlock()
|
||||
|
||||
ft.clock.removeTimeWatcher(ft)
|
||||
|
||||
return active
|
||||
}
|
||||
|
||||
func (ft *fakeTimer) shouldFire(now time.Time) bool {
|
||||
ft.mutex.Lock()
|
||||
defer ft.mutex.Unlock()
|
||||
|
||||
if ft.completionTime.IsZero() {
|
||||
return false
|
||||
}
|
||||
|
||||
return now.After(ft.completionTime) || now.Equal(ft.completionTime)
|
||||
}
|
||||
|
||||
func (ft *fakeTimer) repeatable() bool {
|
||||
return ft.repeat
|
||||
}
|
||||
|
||||
func (ft *fakeTimer) timeUpdated(now time.Time) {
|
||||
select {
|
||||
case ft.channel <- now:
|
||||
default:
|
||||
// drop on the floor. timers have a buffered channel anyway. according to
|
||||
// godoc of the `time' package a ticker can loose ticks in case of a slow
|
||||
// receiver
|
||||
}
|
||||
|
||||
if ft.repeatable() {
|
||||
ft.reset(ft.duration)
|
||||
}
|
||||
}
|
||||
1
vendor/code.cloudfoundry.org/clock/fakeclock/package.go
generated
vendored
Normal file
1
vendor/code.cloudfoundry.org/clock/fakeclock/package.go
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package fakeclock // import "code.cloudfoundry.org/clock/fakeclock"
|
||||
26
vendor/github.com/bits-and-blooms/bitset/.gitignore
generated
vendored
Normal file
26
vendor/github.com/bits-and-blooms/bitset/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
target
|
||||
37
vendor/github.com/bits-and-blooms/bitset/.travis.yml
generated
vendored
Normal file
37
vendor/github.com/bits-and-blooms/bitset/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
language: go
|
||||
|
||||
sudo: false
|
||||
|
||||
branches:
|
||||
except:
|
||||
- release
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- travis
|
||||
|
||||
go:
|
||||
- "1.11.x"
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
before_install:
|
||||
- if [ -n "$GH_USER" ]; then git config --global github.user ${GH_USER}; fi;
|
||||
- if [ -n "$GH_TOKEN" ]; then git config --global github.token ${GH_TOKEN}; fi;
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
before_script:
|
||||
- make deps
|
||||
|
||||
script:
|
||||
- make qa
|
||||
|
||||
after_failure:
|
||||
- cat ./target/test/report.xml
|
||||
|
||||
after_success:
|
||||
- if [ "$TRAVIS_GO_VERSION" = "1.11.1" ]; then $HOME/gopath/bin/goveralls -covermode=count -coverprofile=target/report/coverage.out -service=travis-ci; fi;
|
||||
27
vendor/github.com/bits-and-blooms/bitset/LICENSE
generated
vendored
Normal file
27
vendor/github.com/bits-and-blooms/bitset/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2014 Will Fitzgerald. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
159
vendor/github.com/bits-and-blooms/bitset/README.md
generated
vendored
Normal file
159
vendor/github.com/bits-and-blooms/bitset/README.md
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
# bitset
|
||||
|
||||
*Go language library to map between non-negative integers and boolean values*
|
||||
|
||||
[](https://github.com/willf/bitset/actions?query=workflow%3ATest)
|
||||
[](https://goreportcard.com/report/github.com/willf/bitset)
|
||||
[](https://pkg.go.dev/github.com/bits-and-blooms/bitset?tab=doc)
|
||||
|
||||
|
||||
This library is part of the [awesome go collection](https://github.com/avelino/awesome-go). It is used in production by several important systems:
|
||||
|
||||
* [beego](https://github.com/beego/beego)
|
||||
* [CubeFS](https://github.com/cubefs/cubefs)
|
||||
* [Amazon EKS Distro](https://github.com/aws/eks-distro)
|
||||
* [sourcegraph](https://github.com/sourcegraph/sourcegraph)
|
||||
* [torrent](https://github.com/anacrolix/torrent)
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
Package bitset implements bitsets, a mapping between non-negative integers and boolean values.
|
||||
It should be more efficient than map[uint] bool.
|
||||
|
||||
It provides methods for setting, clearing, flipping, and testing individual integers.
|
||||
|
||||
But it also provides set intersection, union, difference, complement, and symmetric operations, as well as tests to check whether any, all, or no bits are set, and querying a bitset's current length and number of positive bits.
|
||||
|
||||
BitSets are expanded to the size of the largest set bit; the memory allocation is approximately Max bits, where Max is the largest set bit. BitSets are never shrunk. On creation, a hint can be given for the number of bits that will be used.
|
||||
|
||||
Many of the methods, including Set, Clear, and Flip, return a BitSet pointer, which allows for chaining.
|
||||
|
||||
### Example use:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/bits-and-blooms/bitset"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Hello from BitSet!\n")
|
||||
var b bitset.BitSet
|
||||
// play some Go Fish
|
||||
for i := 0; i < 100; i++ {
|
||||
card1 := uint(rand.Intn(52))
|
||||
card2 := uint(rand.Intn(52))
|
||||
b.Set(card1)
|
||||
if b.Test(card2) {
|
||||
fmt.Println("Go Fish!")
|
||||
}
|
||||
b.Clear(card1)
|
||||
}
|
||||
|
||||
// Chaining
|
||||
b.Set(10).Set(11)
|
||||
|
||||
for i, e := b.NextSet(0); e; i, e = b.NextSet(i + 1) {
|
||||
fmt.Println("The following bit is set:", i)
|
||||
}
|
||||
if b.Intersection(bitset.New(100).Set(10)).Count() == 1 {
|
||||
fmt.Println("Intersection works.")
|
||||
} else {
|
||||
fmt.Println("Intersection doesn't work???")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Package documentation is at: https://pkg.go.dev/github.com/bits-and-blooms/bitset?tab=doc
|
||||
|
||||
## Serialization
|
||||
|
||||
|
||||
You may serialize a bitset safely and portably to a stream
|
||||
of bytes as follows:
|
||||
```Go
|
||||
const length = 9585
|
||||
const oneEvery = 97
|
||||
bs := bitset.New(length)
|
||||
// Add some bits
|
||||
for i := uint(0); i < length; i += oneEvery {
|
||||
bs = bs.Set(i)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
n, err := bs.WriteTo(&buf)
|
||||
if err != nil {
|
||||
// failure
|
||||
}
|
||||
// Here n == buf.Len()
|
||||
```
|
||||
You can later deserialize the result as follows:
|
||||
|
||||
```Go
|
||||
// Read back from buf
|
||||
bs = bitset.New()
|
||||
n, err = bs.ReadFrom(&buf)
|
||||
if err != nil {
|
||||
// error
|
||||
}
|
||||
// n is the number of bytes read
|
||||
```
|
||||
|
||||
The `ReadFrom` function attempts to read the data into the existing
|
||||
BitSet instance, to minimize memory allocations.
|
||||
|
||||
|
||||
*Performance tip*:
|
||||
When reading and writing to a file or a network connection, you may get better performance by
|
||||
wrapping your streams with `bufio` instances.
|
||||
|
||||
E.g.,
|
||||
```Go
|
||||
f, err := os.Create("myfile")
|
||||
w := bufio.NewWriter(f)
|
||||
```
|
||||
```Go
|
||||
f, err := os.Open("myfile")
|
||||
r := bufio.NewReader(f)
|
||||
```
|
||||
|
||||
## Memory Usage
|
||||
|
||||
The memory usage of a bitset using `N` bits is at least `N/8` bytes. The number of bits in a bitset is at least as large as one plus the greatest bit index you have accessed. Thus it is possible to run out of memory while using a bitset. If you have lots of bits, you might prefer compressed bitsets, like the [Roaring bitmaps](http://roaringbitmap.org) and its [Go implementation](https://github.com/RoaringBitmap/roaring).
|
||||
|
||||
The `roaring` library allows you to go back and forth between compressed Roaring bitmaps and the conventional bitset instances:
|
||||
```Go
|
||||
mybitset := roaringbitmap.ToBitSet()
|
||||
newroaringbitmap := roaring.FromBitSet(mybitset)
|
||||
```
|
||||
|
||||
|
||||
## Implementation Note
|
||||
|
||||
Go 1.9 introduced a native `math/bits` library. We provide backward compatibility to Go 1.7, which might be removed.
|
||||
|
||||
It is possible that a later version will match the `math/bits` return signature for counts (which is `int`, rather than our library's `uint64`). If so, the version will be bumped.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go get github.com/bits-and-blooms/bitset
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
If you wish to contribute to this project, please branch and issue a pull request against master ("[GitHub Flow](https://guides.github.com/introduction/flow/)")
|
||||
|
||||
## Running all tests
|
||||
|
||||
Before committing the code, please check if it passes tests, has adequate coverage, etc.
|
||||
```bash
|
||||
go test
|
||||
go test -cover
|
||||
```
|
||||
5
vendor/github.com/bits-and-blooms/bitset/SECURITY.md
generated
vendored
Normal file
5
vendor/github.com/bits-and-blooms/bitset/SECURITY.md
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
You can report privately a vulnerability by email at daniel@lemire.me (current maintainer).
|
||||
39
vendor/github.com/bits-and-blooms/bitset/azure-pipelines.yml
generated
vendored
Normal file
39
vendor/github.com/bits-and-blooms/bitset/azure-pipelines.yml
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# Go
|
||||
# Build your Go project.
|
||||
# Add steps that test, save build artifacts, deploy, and more:
|
||||
# https://docs.microsoft.com/azure/devops/pipelines/languages/go
|
||||
|
||||
trigger:
|
||||
- master
|
||||
|
||||
pool:
|
||||
vmImage: 'Ubuntu-16.04'
|
||||
|
||||
variables:
|
||||
GOBIN: '$(GOPATH)/bin' # Go binaries path
|
||||
GOROOT: '/usr/local/go1.11' # Go installation path
|
||||
GOPATH: '$(system.defaultWorkingDirectory)/gopath' # Go workspace path
|
||||
modulePath: '$(GOPATH)/src/github.com/$(build.repository.name)' # Path to the module's code
|
||||
|
||||
steps:
|
||||
- script: |
|
||||
mkdir -p '$(GOBIN)'
|
||||
mkdir -p '$(GOPATH)/pkg'
|
||||
mkdir -p '$(modulePath)'
|
||||
shopt -s extglob
|
||||
shopt -s dotglob
|
||||
mv !(gopath) '$(modulePath)'
|
||||
echo '##vso[task.prependpath]$(GOBIN)'
|
||||
echo '##vso[task.prependpath]$(GOROOT)/bin'
|
||||
displayName: 'Set up the Go workspace'
|
||||
|
||||
- script: |
|
||||
go version
|
||||
go get -v -t -d ./...
|
||||
if [ -f Gopkg.toml ]; then
|
||||
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
dep ensure
|
||||
fi
|
||||
go build -v .
|
||||
workingDirectory: '$(modulePath)'
|
||||
displayName: 'Get dependencies, then build'
|
||||
1184
vendor/github.com/bits-and-blooms/bitset/bitset.go
generated
vendored
Normal file
1184
vendor/github.com/bits-and-blooms/bitset/bitset.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
53
vendor/github.com/bits-and-blooms/bitset/popcnt.go
generated
vendored
Normal file
53
vendor/github.com/bits-and-blooms/bitset/popcnt.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
package bitset
|
||||
|
||||
// bit population count, take from
|
||||
// https://code.google.com/p/go/issues/detail?id=4988#c11
|
||||
// credit: https://code.google.com/u/arnehormann/
|
||||
func popcount(x uint64) (n uint64) {
|
||||
x -= (x >> 1) & 0x5555555555555555
|
||||
x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
|
||||
x += x >> 4
|
||||
x &= 0x0f0f0f0f0f0f0f0f
|
||||
x *= 0x0101010101010101
|
||||
return x >> 56
|
||||
}
|
||||
|
||||
func popcntSliceGo(s []uint64) uint64 {
|
||||
cnt := uint64(0)
|
||||
for _, x := range s {
|
||||
cnt += popcount(x)
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
|
||||
func popcntMaskSliceGo(s, m []uint64) uint64 {
|
||||
cnt := uint64(0)
|
||||
for i := range s {
|
||||
cnt += popcount(s[i] &^ m[i])
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
|
||||
func popcntAndSliceGo(s, m []uint64) uint64 {
|
||||
cnt := uint64(0)
|
||||
for i := range s {
|
||||
cnt += popcount(s[i] & m[i])
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
|
||||
func popcntOrSliceGo(s, m []uint64) uint64 {
|
||||
cnt := uint64(0)
|
||||
for i := range s {
|
||||
cnt += popcount(s[i] | m[i])
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
|
||||
func popcntXorSliceGo(s, m []uint64) uint64 {
|
||||
cnt := uint64(0)
|
||||
for i := range s {
|
||||
cnt += popcount(s[i] ^ m[i])
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
62
vendor/github.com/bits-and-blooms/bitset/popcnt_19.go
generated
vendored
Normal file
62
vendor/github.com/bits-and-blooms/bitset/popcnt_19.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
//go:build go1.9
|
||||
// +build go1.9
|
||||
|
||||
package bitset
|
||||
|
||||
import "math/bits"
|
||||
|
||||
func popcntSlice(s []uint64) uint64 {
|
||||
var cnt int
|
||||
for _, x := range s {
|
||||
cnt += bits.OnesCount64(x)
|
||||
}
|
||||
return uint64(cnt)
|
||||
}
|
||||
|
||||
func popcntMaskSlice(s, m []uint64) uint64 {
|
||||
var cnt int
|
||||
// this explicit check eliminates a bounds check in the loop
|
||||
if len(m) < len(s) {
|
||||
panic("mask slice is too short")
|
||||
}
|
||||
for i := range s {
|
||||
cnt += bits.OnesCount64(s[i] &^ m[i])
|
||||
}
|
||||
return uint64(cnt)
|
||||
}
|
||||
|
||||
func popcntAndSlice(s, m []uint64) uint64 {
|
||||
var cnt int
|
||||
// this explicit check eliminates a bounds check in the loop
|
||||
if len(m) < len(s) {
|
||||
panic("mask slice is too short")
|
||||
}
|
||||
for i := range s {
|
||||
cnt += bits.OnesCount64(s[i] & m[i])
|
||||
}
|
||||
return uint64(cnt)
|
||||
}
|
||||
|
||||
func popcntOrSlice(s, m []uint64) uint64 {
|
||||
var cnt int
|
||||
// this explicit check eliminates a bounds check in the loop
|
||||
if len(m) < len(s) {
|
||||
panic("mask slice is too short")
|
||||
}
|
||||
for i := range s {
|
||||
cnt += bits.OnesCount64(s[i] | m[i])
|
||||
}
|
||||
return uint64(cnt)
|
||||
}
|
||||
|
||||
func popcntXorSlice(s, m []uint64) uint64 {
|
||||
var cnt int
|
||||
// this explicit check eliminates a bounds check in the loop
|
||||
if len(m) < len(s) {
|
||||
panic("mask slice is too short")
|
||||
}
|
||||
for i := range s {
|
||||
cnt += bits.OnesCount64(s[i] ^ m[i])
|
||||
}
|
||||
return uint64(cnt)
|
||||
}
|
||||
68
vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.go
generated
vendored
Normal file
68
vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
//go:build !go1.9 && amd64 && !appengine
|
||||
// +build !go1.9,amd64,!appengine
|
||||
|
||||
package bitset
|
||||
|
||||
// *** the following functions are defined in popcnt_amd64.s
|
||||
|
||||
//go:noescape
|
||||
|
||||
func hasAsm() bool
|
||||
|
||||
// useAsm is a flag used to select the GO or ASM implementation of the popcnt function
|
||||
var useAsm = hasAsm()
|
||||
|
||||
//go:noescape
|
||||
|
||||
func popcntSliceAsm(s []uint64) uint64
|
||||
|
||||
//go:noescape
|
||||
|
||||
func popcntMaskSliceAsm(s, m []uint64) uint64
|
||||
|
||||
//go:noescape
|
||||
|
||||
func popcntAndSliceAsm(s, m []uint64) uint64
|
||||
|
||||
//go:noescape
|
||||
|
||||
func popcntOrSliceAsm(s, m []uint64) uint64
|
||||
|
||||
//go:noescape
|
||||
|
||||
func popcntXorSliceAsm(s, m []uint64) uint64
|
||||
|
||||
func popcntSlice(s []uint64) uint64 {
|
||||
if useAsm {
|
||||
return popcntSliceAsm(s)
|
||||
}
|
||||
return popcntSliceGo(s)
|
||||
}
|
||||
|
||||
func popcntMaskSlice(s, m []uint64) uint64 {
|
||||
if useAsm {
|
||||
return popcntMaskSliceAsm(s, m)
|
||||
}
|
||||
return popcntMaskSliceGo(s, m)
|
||||
}
|
||||
|
||||
func popcntAndSlice(s, m []uint64) uint64 {
|
||||
if useAsm {
|
||||
return popcntAndSliceAsm(s, m)
|
||||
}
|
||||
return popcntAndSliceGo(s, m)
|
||||
}
|
||||
|
||||
func popcntOrSlice(s, m []uint64) uint64 {
|
||||
if useAsm {
|
||||
return popcntOrSliceAsm(s, m)
|
||||
}
|
||||
return popcntOrSliceGo(s, m)
|
||||
}
|
||||
|
||||
func popcntXorSlice(s, m []uint64) uint64 {
|
||||
if useAsm {
|
||||
return popcntXorSliceAsm(s, m)
|
||||
}
|
||||
return popcntXorSliceGo(s, m)
|
||||
}
|
||||
104
vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.s
generated
vendored
Normal file
104
vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.s
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
// +build !go1.9
|
||||
// +build amd64,!appengine
|
||||
|
||||
TEXT ·hasAsm(SB),4,$0-1
|
||||
MOVQ $1, AX
|
||||
CPUID
|
||||
SHRQ $23, CX
|
||||
ANDQ $1, CX
|
||||
MOVB CX, ret+0(FP)
|
||||
RET
|
||||
|
||||
#define POPCNTQ_DX_DX BYTE $0xf3; BYTE $0x48; BYTE $0x0f; BYTE $0xb8; BYTE $0xd2
|
||||
|
||||
TEXT ·popcntSliceAsm(SB),4,$0-32
|
||||
XORQ AX, AX
|
||||
MOVQ s+0(FP), SI
|
||||
MOVQ s_len+8(FP), CX
|
||||
TESTQ CX, CX
|
||||
JZ popcntSliceEnd
|
||||
popcntSliceLoop:
|
||||
BYTE $0xf3; BYTE $0x48; BYTE $0x0f; BYTE $0xb8; BYTE $0x16 // POPCNTQ (SI), DX
|
||||
ADDQ DX, AX
|
||||
ADDQ $8, SI
|
||||
LOOP popcntSliceLoop
|
||||
popcntSliceEnd:
|
||||
MOVQ AX, ret+24(FP)
|
||||
RET
|
||||
|
||||
TEXT ·popcntMaskSliceAsm(SB),4,$0-56
|
||||
XORQ AX, AX
|
||||
MOVQ s+0(FP), SI
|
||||
MOVQ s_len+8(FP), CX
|
||||
TESTQ CX, CX
|
||||
JZ popcntMaskSliceEnd
|
||||
MOVQ m+24(FP), DI
|
||||
popcntMaskSliceLoop:
|
||||
MOVQ (DI), DX
|
||||
NOTQ DX
|
||||
ANDQ (SI), DX
|
||||
POPCNTQ_DX_DX
|
||||
ADDQ DX, AX
|
||||
ADDQ $8, SI
|
||||
ADDQ $8, DI
|
||||
LOOP popcntMaskSliceLoop
|
||||
popcntMaskSliceEnd:
|
||||
MOVQ AX, ret+48(FP)
|
||||
RET
|
||||
|
||||
TEXT ·popcntAndSliceAsm(SB),4,$0-56
|
||||
XORQ AX, AX
|
||||
MOVQ s+0(FP), SI
|
||||
MOVQ s_len+8(FP), CX
|
||||
TESTQ CX, CX
|
||||
JZ popcntAndSliceEnd
|
||||
MOVQ m+24(FP), DI
|
||||
popcntAndSliceLoop:
|
||||
MOVQ (DI), DX
|
||||
ANDQ (SI), DX
|
||||
POPCNTQ_DX_DX
|
||||
ADDQ DX, AX
|
||||
ADDQ $8, SI
|
||||
ADDQ $8, DI
|
||||
LOOP popcntAndSliceLoop
|
||||
popcntAndSliceEnd:
|
||||
MOVQ AX, ret+48(FP)
|
||||
RET
|
||||
|
||||
TEXT ·popcntOrSliceAsm(SB),4,$0-56
|
||||
XORQ AX, AX
|
||||
MOVQ s+0(FP), SI
|
||||
MOVQ s_len+8(FP), CX
|
||||
TESTQ CX, CX
|
||||
JZ popcntOrSliceEnd
|
||||
MOVQ m+24(FP), DI
|
||||
popcntOrSliceLoop:
|
||||
MOVQ (DI), DX
|
||||
ORQ (SI), DX
|
||||
POPCNTQ_DX_DX
|
||||
ADDQ DX, AX
|
||||
ADDQ $8, SI
|
||||
ADDQ $8, DI
|
||||
LOOP popcntOrSliceLoop
|
||||
popcntOrSliceEnd:
|
||||
MOVQ AX, ret+48(FP)
|
||||
RET
|
||||
|
||||
TEXT ·popcntXorSliceAsm(SB),4,$0-56
|
||||
XORQ AX, AX
|
||||
MOVQ s+0(FP), SI
|
||||
MOVQ s_len+8(FP), CX
|
||||
TESTQ CX, CX
|
||||
JZ popcntXorSliceEnd
|
||||
MOVQ m+24(FP), DI
|
||||
popcntXorSliceLoop:
|
||||
MOVQ (DI), DX
|
||||
XORQ (SI), DX
|
||||
POPCNTQ_DX_DX
|
||||
ADDQ DX, AX
|
||||
ADDQ $8, SI
|
||||
ADDQ $8, DI
|
||||
LOOP popcntXorSliceLoop
|
||||
popcntXorSliceEnd:
|
||||
MOVQ AX, ret+48(FP)
|
||||
RET
|
||||
25
vendor/github.com/bits-and-blooms/bitset/popcnt_generic.go
generated
vendored
Normal file
25
vendor/github.com/bits-and-blooms/bitset/popcnt_generic.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
//go:build !go1.9 && (!amd64 || appengine)
|
||||
// +build !go1.9
|
||||
// +build !amd64 appengine
|
||||
|
||||
package bitset
|
||||
|
||||
func popcntSlice(s []uint64) uint64 {
|
||||
return popcntSliceGo(s)
|
||||
}
|
||||
|
||||
func popcntMaskSlice(s, m []uint64) uint64 {
|
||||
return popcntMaskSliceGo(s, m)
|
||||
}
|
||||
|
||||
func popcntAndSlice(s, m []uint64) uint64 {
|
||||
return popcntAndSliceGo(s, m)
|
||||
}
|
||||
|
||||
func popcntOrSlice(s, m []uint64) uint64 {
|
||||
return popcntOrSliceGo(s, m)
|
||||
}
|
||||
|
||||
func popcntXorSlice(s, m []uint64) uint64 {
|
||||
return popcntXorSliceGo(s, m)
|
||||
}
|
||||
45
vendor/github.com/bits-and-blooms/bitset/select.go
generated
vendored
Normal file
45
vendor/github.com/bits-and-blooms/bitset/select.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package bitset
|
||||
|
||||
func select64(w uint64, j uint) uint {
|
||||
seen := 0
|
||||
// Divide 64bit
|
||||
part := w & 0xFFFFFFFF
|
||||
n := uint(popcount(part))
|
||||
if n <= j {
|
||||
part = w >> 32
|
||||
seen += 32
|
||||
j -= n
|
||||
}
|
||||
ww := part
|
||||
|
||||
// Divide 32bit
|
||||
part = ww & 0xFFFF
|
||||
|
||||
n = uint(popcount(part))
|
||||
if n <= j {
|
||||
part = ww >> 16
|
||||
seen += 16
|
||||
j -= n
|
||||
}
|
||||
ww = part
|
||||
|
||||
// Divide 16bit
|
||||
part = ww & 0xFF
|
||||
n = uint(popcount(part))
|
||||
if n <= j {
|
||||
part = ww >> 8
|
||||
seen += 8
|
||||
j -= n
|
||||
}
|
||||
ww = part
|
||||
|
||||
// Lookup in final byte
|
||||
counter := 0
|
||||
for ; counter < 8; counter++ {
|
||||
j -= uint((ww >> counter) & 1)
|
||||
if j+1 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return uint(seen + counter)
|
||||
}
|
||||
15
vendor/github.com/bits-and-blooms/bitset/trailing_zeros_18.go
generated
vendored
Normal file
15
vendor/github.com/bits-and-blooms/bitset/trailing_zeros_18.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
//go:build !go1.9
|
||||
// +build !go1.9
|
||||
|
||||
package bitset
|
||||
|
||||
var deBruijn = [...]byte{
|
||||
0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
|
||||
62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
|
||||
63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
|
||||
54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
|
||||
}
|
||||
|
||||
func trailingZeroes64(v uint64) uint {
|
||||
return uint(deBruijn[((v&-v)*0x03f79d71b4ca8b09)>>58])
|
||||
}
|
||||
10
vendor/github.com/bits-and-blooms/bitset/trailing_zeros_19.go
generated
vendored
Normal file
10
vendor/github.com/bits-and-blooms/bitset/trailing_zeros_19.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
//go:build go1.9
|
||||
// +build go1.9
|
||||
|
||||
package bitset
|
||||
|
||||
import "math/bits"
|
||||
|
||||
func trailingZeroes64(v uint64) uint {
|
||||
return uint(bits.TrailingZeros64(v))
|
||||
}
|
||||
3
vendor/github.com/containerd/containerd/RELEASES.md
generated
vendored
3
vendor/github.com/containerd/containerd/RELEASES.md
generated
vendored
@@ -394,6 +394,9 @@ The deprecated properties in [`config.toml`](./docs/cri/config.md) are shown in
|
||||
|`[plugins."io.containerd.grpc.v1.cri".registry]` | `auths` | containerd v1.3 | containerd v2.0 | Use [`ImagePullSecrets`](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). See also [#8228](https://github.com/containerd/containerd/issues/8228). |
|
||||
|`[plugins."io.containerd.grpc.v1.cri".registry]` | `configs` | containerd v1.5 | containerd v2.0 | Use [`config_path`](./docs/hosts.md) |
|
||||
|`[plugins."io.containerd.grpc.v1.cri".registry]` | `mirrors` | containerd v1.5 | containerd v2.0 | Use [`config_path`](./docs/hosts.md) |
|
||||
|`[plugins."io.containerd.tracing.processor.v1.otlp"]` | `endpoint`, `protocol`, `insecure` | containerd v1.6.29 | containerd v2.0 | Use [OTLP environment variables](https://opentelemetry.io/docs/specs/otel/protocol/exporter/), e.g. OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_SDK_DISABLED |
|
||||
|`[plugins."io.containerd.internal.v1.tracing"]` | `service_name`, `sampling_ratio` | containerd v1.6.29 | containerd v2.0 | Instead use [OTel environment variables](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/), e.g. OTEL_SERVICE_NAME, OTEL_TRACES_SAMPLER* |
|
||||
|
||||
|
||||
> **Note**
|
||||
>
|
||||
|
||||
2
vendor/github.com/containerd/containerd/Vagrantfile
generated
vendored
2
vendor/github.com/containerd/containerd/Vagrantfile
generated
vendored
@@ -102,7 +102,7 @@ EOF
|
||||
config.vm.provision "install-golang", type: "shell", run: "once" do |sh|
|
||||
sh.upload_path = "/tmp/vagrant-install-golang"
|
||||
sh.env = {
|
||||
'GO_VERSION': ENV['GO_VERSION'] || "1.21.8",
|
||||
'GO_VERSION': ENV['GO_VERSION'] || "1.21.9",
|
||||
}
|
||||
sh.inline = <<~SHELL
|
||||
#!/usr/bin/env bash
|
||||
|
||||
8
vendor/github.com/containerd/containerd/pkg/deprecation/deprecation.go
generated
vendored
8
vendor/github.com/containerd/containerd/pkg/deprecation/deprecation.go
generated
vendored
@@ -53,6 +53,10 @@ const (
|
||||
RuntimeRuncV1 Warning = Prefix + "runtime-runc-v1"
|
||||
// CRICRIUPath is a warning for the use of the `CriuPath` property
|
||||
CRICRIUPath Warning = Prefix + "cri-criu-path"
|
||||
// OTLPTracingConfig is a warning for the use of the `otlp` property
|
||||
TracingOTLPConfig Warning = Prefix + "tracing-processor-config"
|
||||
// TracingServiceConfig is a warning for the use of the `tracing` property
|
||||
TracingServiceConfig Warning = Prefix + "tracing-service-config"
|
||||
)
|
||||
|
||||
var messages = map[Warning]string{
|
||||
@@ -82,6 +86,10 @@ var messages = map[Warning]string{
|
||||
RuntimeRuncV1: "The `io.containerd.runc.v1` runtime is deprecated since containerd v1.4 and removed in containerd v2.0. Use the `io.containerd.runc.v2` runtime instead.",
|
||||
CRICRIUPath: "The `CriuPath` property of `[plugins.\"io.containerd.grpc.v1.cri\".containerd.runtimes.*.options]` is deprecated since containerd v1.7 and will be removed in containerd v2.0. " +
|
||||
"Use a criu binary in $PATH instead.",
|
||||
TracingOTLPConfig: "The `otlp` property of `[plugins.\"io.containerd.tracing.processor.v1\".otlp]` is deprecated since containerd v1.6 and will be removed in containerd v2.0." +
|
||||
"Use OTLP environment variables instead: https://opentelemetry.io/docs/specs/otel/protocol/exporter/",
|
||||
TracingServiceConfig: "The `tracing` property of `[plugins.\"io.containerd.internal.v1\".tracing]` is deprecated since containerd v1.6 and will be removed in containerd v2.0." +
|
||||
"Use OTEL environment variables instead: https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/",
|
||||
}
|
||||
|
||||
// Valid checks whether a given Warning is valid
|
||||
|
||||
4
vendor/github.com/containerd/containerd/plugin/plugin.go
generated
vendored
4
vendor/github.com/containerd/containerd/plugin/plugin.go
generated
vendored
@@ -104,6 +104,10 @@ const (
|
||||
DeprecationsPlugin = "deprecations"
|
||||
)
|
||||
|
||||
const (
|
||||
SnapshotterRootDir = "root"
|
||||
)
|
||||
|
||||
// Registration contains information for registering a plugin
|
||||
type Registration struct {
|
||||
// Type of the plugin
|
||||
|
||||
18
vendor/github.com/containerd/containerd/remotes/docker/authorizer.go
generated
vendored
18
vendor/github.com/containerd/containerd/remotes/docker/authorizer.go
generated
vendored
@@ -148,9 +148,11 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
|
||||
defer a.mu.Unlock()
|
||||
for _, c := range auth.ParseAuthHeader(last.Header) {
|
||||
if c.Scheme == auth.BearerAuth {
|
||||
if err := invalidAuthorization(c, responses); err != nil {
|
||||
if retry, err := invalidAuthorization(ctx, c, responses); err != nil {
|
||||
delete(a.handlers, host)
|
||||
return err
|
||||
} else if retry {
|
||||
delete(a.handlers, host)
|
||||
}
|
||||
|
||||
// reuse existing handler
|
||||
@@ -328,18 +330,24 @@ func (ah *authHandler) doBearerAuth(ctx context.Context) (token, refreshToken st
|
||||
return resp.Token, resp.RefreshToken, nil
|
||||
}
|
||||
|
||||
func invalidAuthorization(c auth.Challenge, responses []*http.Response) error {
|
||||
func invalidAuthorization(ctx context.Context, c auth.Challenge, responses []*http.Response) (retry bool, _ error) {
|
||||
errStr := c.Parameters["error"]
|
||||
if errStr == "" {
|
||||
return nil
|
||||
return retry, nil
|
||||
}
|
||||
|
||||
n := len(responses)
|
||||
if n == 1 || (n > 1 && !sameRequest(responses[n-2].Request, responses[n-1].Request)) {
|
||||
return nil
|
||||
limitedErr := errStr
|
||||
errLenghLimit := 64
|
||||
if len(limitedErr) > errLenghLimit {
|
||||
limitedErr = limitedErr[:errLenghLimit] + "..."
|
||||
}
|
||||
log.G(ctx).WithField("error", limitedErr).Debug("authorization error using bearer token, retrying")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("server message: %s: %w", errStr, ErrInvalidAuthorization)
|
||||
return retry, fmt.Errorf("server message: %s: %w", errStr, ErrInvalidAuthorization)
|
||||
}
|
||||
|
||||
func sameRequest(r1, r2 *http.Request) bool {
|
||||
|
||||
70
vendor/github.com/containerd/containerd/remotes/docker/resolver.go
generated
vendored
70
vendor/github.com/containerd/containerd/remotes/docker/resolver.go
generated
vendored
@@ -704,9 +704,71 @@ func IsLocalhost(host string) bool {
|
||||
return ip.IsLoopback()
|
||||
}
|
||||
|
||||
// NewHTTPFallback returns http.RoundTripper which allows fallback from https to
|
||||
// http for registry endpoints with configurations for both http and TLS,
|
||||
// such as defaulted localhost endpoints.
|
||||
func NewHTTPFallback(transport http.RoundTripper) http.RoundTripper {
|
||||
return &httpFallback{
|
||||
super: transport,
|
||||
}
|
||||
}
|
||||
|
||||
type httpFallback struct {
|
||||
super http.RoundTripper
|
||||
host string
|
||||
}
|
||||
|
||||
func (f *httpFallback) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
// only fall back if the same host had previously fell back
|
||||
if f.host != r.URL.Host {
|
||||
resp, err := f.super.RoundTrip(r)
|
||||
if !isTLSError(err) {
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
plainHTTPUrl := *r.URL
|
||||
plainHTTPUrl.Scheme = "http"
|
||||
|
||||
plainHTTPRequest := *r
|
||||
plainHTTPRequest.URL = &plainHTTPUrl
|
||||
|
||||
if f.host != r.URL.Host {
|
||||
f.host = r.URL.Host
|
||||
|
||||
// update body on the second attempt
|
||||
if r.Body != nil && r.GetBody != nil {
|
||||
body, err := r.GetBody()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plainHTTPRequest.Body = body
|
||||
}
|
||||
}
|
||||
|
||||
return f.super.RoundTrip(&plainHTTPRequest)
|
||||
}
|
||||
|
||||
func isTLSError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var tlsErr tls.RecordHeaderError
|
||||
if errors.As(err, &tlsErr) && string(tlsErr.RecordHeader[:]) == "HTTP/" {
|
||||
return true
|
||||
}
|
||||
if strings.Contains(err.Error(), "TLS handshake timeout") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HTTPFallback is an http.RoundTripper which allows fallback from https to http
|
||||
// for registry endpoints with configurations for both http and TLS, such as
|
||||
// defaulted localhost endpoints.
|
||||
//
|
||||
// Deprecated: Use NewHTTPFallback instead.
|
||||
type HTTPFallback struct {
|
||||
http.RoundTripper
|
||||
}
|
||||
@@ -722,6 +784,14 @@ func (f HTTPFallback) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
plainHTTPRequest := *r
|
||||
plainHTTPRequest.URL = &plainHTTPUrl
|
||||
|
||||
if r.Body != nil && r.GetBody != nil {
|
||||
body, err := r.GetBody()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plainHTTPRequest.Body = body
|
||||
}
|
||||
|
||||
return f.RoundTripper.RoundTrip(&plainHTTPRequest)
|
||||
}
|
||||
|
||||
|
||||
18
vendor/github.com/containerd/containerd/services/server/config/config.go
generated
vendored
18
vendor/github.com/containerd/containerd/services/server/config/config.go
generated
vendored
@@ -164,8 +164,10 @@ type CgroupConfig struct {
|
||||
|
||||
// ProxyPlugin provides a proxy plugin configuration
|
||||
type ProxyPlugin struct {
|
||||
Type string `toml:"type"`
|
||||
Address string `toml:"address"`
|
||||
Type string `toml:"type"`
|
||||
Address string `toml:"address"`
|
||||
Platform string `toml:"platform"`
|
||||
Exports map[string]string `toml:"exports"`
|
||||
}
|
||||
|
||||
// Decode unmarshals a plugin specific configuration by plugin id
|
||||
@@ -251,13 +253,18 @@ func loadConfigFile(path string) (*Config, error) {
|
||||
}
|
||||
|
||||
// resolveImports resolves import strings list to absolute paths list:
|
||||
// - If path contains *, glob pattern matching applied
|
||||
// - Non abs path is relative to parent config file directory
|
||||
// - If path contains *, glob pattern matching applied
|
||||
// - Abs paths returned as is
|
||||
func resolveImports(parent string, imports []string) ([]string, error) {
|
||||
var out []string
|
||||
|
||||
for _, path := range imports {
|
||||
path := filepath.Clean(path)
|
||||
if !filepath.IsAbs(path) {
|
||||
path = filepath.Join(filepath.Dir(parent), path)
|
||||
}
|
||||
|
||||
if strings.Contains(path, "*") {
|
||||
matches, err := filepath.Glob(path)
|
||||
if err != nil {
|
||||
@@ -266,11 +273,6 @@ func resolveImports(parent string, imports []string) ([]string, error) {
|
||||
|
||||
out = append(out, matches...)
|
||||
} else {
|
||||
path = filepath.Clean(path)
|
||||
if !filepath.IsAbs(path) {
|
||||
path = filepath.Join(filepath.Dir(parent), path)
|
||||
}
|
||||
|
||||
out = append(out, path)
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user