Compare commits

..

87 Commits

Author SHA1 Message Date
Albin Kerouanton
8e96db1c32 Merge pull request #47832 from vvoland/v26.1-47821
[26.1 backport] Forward DNS requests into --internal networks
2024-05-15 09:33:02 +02:00
Rob Murray
c21fe3efa3 Forward DNS requests into --internal networks
A recent change to prevent containers only connected to --internal
networks from communicating with external DNS servers inadvertently
prevented the daemon's internal DNS server from forwarding requests
within an internal network to a containerised DNS server.

Relax the check, so that only requests that need to be forwarded
from the host's network namespace are dropped.

External DNS servers remain unreachable from the internal network.

Signed-off-by: Rob Murray <rob.murray@docker.com>
(cherry picked from commit 77a47dba3b)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-05-14 15:17:56 +02:00
Sebastiaan van Stijn
86af4eddb3 Merge pull request #47833 from vvoland/v26.1-47744
[26.1 backport] Do not forward DNS requests to self.
2024-05-14 15:14:02 +02:00
Sebastiaan van Stijn
73511cdee0 Merge pull request #47829 from vvoland/v26.1-47749
[26.1 backport] apparmor: Allow confined runc to kill containers
2024-05-14 14:12:35 +02:00
Rob Murray
9326cda7bf Do not forward DNS requests to self.
If a container is configured with the internal DNS resolver's own
address as an external server, try the next ext server rather than
recursing (return SERVFAIL if there are no other servers).

Signed-off-by: Rob Murray <rob.murray@docker.com>
(cherry picked from commit 87506142d8)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-05-14 12:27:01 +02:00
Tomáš Virtus
76fcf9a8e0 apparmor: Allow confined runc to kill containers
/usr/sbin/runc is confined with "runc" profile[1] introduced in AppArmor
v4.0.0. This change breaks stopping of containers, because the profile
assigned to containers doesn't accept signals from the "runc" peer.
AppArmor >= v4.0.0 is currently part of Ubuntu Mantic (23.10) and later.

In the case of Docker, this regression is hidden by the fact that
dockerd itself sends SIGKILL to the running container after runc fails
to stop it. It is still a regression, because graceful shutdowns of
containers via "docker stop" are no longer possible, as SIGTERM from
runc is not delivered to them. This can be seen in logs from dockerd
when run with debug logging enabled and also from tracing signals with
killsnoop utility from bcc[2] (in bpfcc-tools package in Debian/Ubuntu):

  Test commands:

    root@cloudimg:~# docker run -d --name test redis
    ba04c137827df8468358c274bc719bf7fc291b1ed9acf4aaa128ccc52816fe46
    root@cloudimg:~# docker stop test

  Relevant syslog messages (with wrapped long lines):

    Apr 23 20:45:26 cloudimg kernel: audit:
      type=1400 audit(1713905126.444:253): apparmor="DENIED"
      operation="signal" class="signal" profile="docker-default" pid=9289
      comm="runc" requested_mask="receive" denied_mask="receive"
      signal=kill peer="runc"
    Apr 23 20:45:36 cloudimg dockerd[9030]:
      time="2024-04-23T20:45:36.447016467Z"
      level=warning msg="Container failed to exit within 10s of kill - trying direct SIGKILL"
      container=ba04c137827df8468358c274bc719bf7fc291b1ed9acf4aaa128ccc52816fe46
      error="context deadline exceeded"

  Killsnoop output after "docker stop ...":

    root@cloudimg:~# killsnoop-bpfcc
    TIME      PID      COMM             SIG  TPID     RESULT
    20:51:00  9631     runc             3    9581     -13
    20:51:02  9637     runc             9    9581     -13
    20:51:12  9030     dockerd          9    9581     0

This change extends the docker-default profile with rules that allow
receiving signals from processes that run confined with either runc or
crun profile (crun[4] is an alternative OCI runtime that's also confined
in AppArmor >= v4.0.0, see [1]). It is backward compatible because the
peer value is a regular expression (AARE) so the referenced profile
doesn't have to exist for this profile to successfully compile and load.

Note that the runc profile has an attachment to /usr/sbin/runc. This is
the path where the runc package in Debian/Ubuntu puts the binary. When
the docker-ce package is installed from the upstream repository[3], runc
is installed as part of the containerd.io package at /usr/bin/runc.
Therefore it's still running unconfined and has no issues sending
signals to containers.

[1] https://gitlab.com/apparmor/apparmor/-/commit/2594d936
[2] https://github.com/iovisor/bcc/blob/master/tools/killsnoop.py
[3] https://download.docker.com/linux/ubuntu
[4] https://github.com/containers/crun

Signed-off-by: Tomáš Virtus <nechtom@gmail.com>
(cherry picked from commit 5ebe2c0d6b)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-05-14 11:40:53 +02:00
Paweł Gronowski
ef1912d8b6 Merge pull request #47806 from vvoland/v26.1-47805
[26.1 backport] update to go1.21.10
2024-05-08 10:59:02 +02:00
Paweł Gronowski
10739af81a update to go1.21.10
These minor releases include 2 security fixes following the security policy:

- cmd/go: arbitrary code execution during build on darwin
On Darwin, building a Go module which contains CGO can trigger arbitrary code execution when using the Apple version of ld, due to usage of the -lto_library flag in a "#cgo LDFLAGS" directive.
Thanks to Juho Forsén of Mattermost for reporting this issue.
This is CVE-2024-24787 and Go issue https://go.dev/issue/67119.

- net: malformed DNS message can cause infinite loop
A malformed DNS message in response to a query can cause the Lookup functions to get stuck in an infinite loop.
Thanks to long-name-let-people-remember-you on GitHub for reporting this issue, and to Mateusz Poliwczak for bringing the issue to our attention.
This is CVE-2024-24788 and Go issue https://go.dev/issue/66754.

View the release notes for more information:
https://go.dev/doc/devel/release#go1.22.3

- https://github.com/golang/go/issues?q=milestone%3AGo1.21.10+label%3ACherryPickApproved
- full diff: https://github.com/golang/go/compare/go1.21.9...go1.21.10

**- Description for the changelog**

```markdown changelog
Update Go runtime to 1.21.10
```

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit 6c97e0e0b5)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-05-08 09:39:08 +02:00
Paweł Gronowski
ac2de55998 Merge pull request #47775 from vvoland/v26.1-47771
[26.1 backport] Option to avoid deleting the kernel_ll address from bridges.
2024-04-30 11:46:49 +02:00
Sebastiaan van Stijn
9a2b531127 Merge pull request #47774 from vvoland/v26.1-47769
[26.1 backport] Allow for a read-only "/proc/sys/net".
2024-04-30 10:05:23 +02:00
Rob Murray
2f5bbbe16b Option to avoid deleting the kernel_ll address from bridges.
If env var DOCKER_BRIDGE_PRESERVE_KERNEL_LL=1, don't assign fe80::1/64
to a bridge, and don't delete any link local address with prefix fe80::/64.

Signed-off-by: Rob Murray <rob.murray@docker.com>
(cherry picked from commit 57ada4b848)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-29 21:53:53 +02:00
Rob Murray
40618081f1 Allow for a read-only "/proc/sys/net".
If dockerd runs on a host with a read-only /proc/sys/net filesystem,
it isn't able to enable or disable IPv6 on network interfaces when
attaching a container to a network (including initial networks during
container creation).

In release 26.0.2, a read-only /proc/sys/net meant container creation
failed in all cases.

So, don't attempt to enable/disable IPv6 on an interface if it's already
set appropriately.

If it's not possible to enable IPv6 when it's needed, just log (because
that's what libnetwork has always done if IPv6 is disabled in the
kernel).

If it's not possible to disable IPv6 when it needs to be disabled,
refuse to create the container and raise an error that suggests setting
environment variable "DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE=1", to tell
the daemon it's ok to ignore the problem.

Signed-off-by: Rob Murray <rob.murray@docker.com>
(cherry picked from commit 01ea18f1e3)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-29 21:49:04 +02:00
Paweł Gronowski
21da192ae4 Merge pull request #47767 from austinvazquez/cherry-pick-eeec716e332e5c058dfe70ddc0006d8552f632b2-to-26.1
[26.1 backport] vendor: update containerd to v1.7.16
2024-04-29 12:08:54 +02:00
Derek McGowan
2c91196921 Update containerd to v1.7.16
Includes fix for HTTP fallback

Signed-off-by: Derek McGowan <derek@mcg.dev>
(cherry picked from commit eeec716e33)
Signed-off-by: Austin Vazquez <macedonv@amazon.com>
2024-04-26 16:05:37 +00:00
Paweł Gronowski
a9a8787c93 Merge pull request #47762 from tonistiigi/26.1-update-buildkit-v0.13.2
[26.1] vendor: update buildkit to v0.13.2
2024-04-26 10:31:10 +02:00
Tonis Tiigi
c9689eccf5 vendor: update buildkit to v0.13.2
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2024-04-25 11:19:43 -07:00
Brian Goff
c8af8ebe4a Merge pull request #47738 from vvoland/c8d-walk-image-badimagetarget
c8d/list: Ignore unexpected image target
2024-04-22 07:45:14 -07:00
Paweł Gronowski
7d95fe8db5 c8d/list: Ignore unexpected image target
Don't fail-fast when encountering an image that targets an unexpected
descriptor (neither a manifest nor index). Log a warning instead.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-22 12:58:46 +02:00
Bjorn Neergaard
801fd16e3e Merge pull request #47735 from cpuguy83/better_walk_error
Include more details in errNotManifestOrIndex
2024-04-19 18:16:12 -07:00
Brian Goff
6667e96dad Include more details in errnotManifestOrIndex
This error is returned when attempting to walk a descriptor that
*should* be an index or a manifest.
Without this the error is not very helpful sicne there's no way to tell
what triggered it.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2024-04-19 21:13:05 +00:00
Paweł Gronowski
ee8b788538 Merge pull request #47734 from krissetto/image-history-timestamp-dereference
fix: avoid nil dereference on image history `Created` value
2024-04-19 14:23:02 +02:00
Paweł Gronowski
96c9353e9b Merge pull request #47723 from vvoland/builder-fix-workdir-slash
builder: Fix `WORKDIR` with a trailing slash causing a cache miss
2024-04-19 13:56:40 +02:00
Christopher Petito
ab570ab3d6 nil dereference fix on image history Created value
Issue was caused by the changes here https://github.com/moby/moby/pull/45504
First released in v25.0.0-beta.1

Signed-off-by: Christopher Petito <47751006+krissetto@users.noreply.github.com>
2024-04-19 10:44:30 +00:00
Paweł Gronowski
7532420f3b container/SetupWorkingDirectory: Don't mutate config
Don't mutate the container's `Config.WorkingDir` permanently with a
cleaned path when creating a working directory.

Move the `filepath.Clean` to the `translateWorkingDir` instead.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-19 12:42:20 +02:00
Paweł Gronowski
a4d5b6b4d0 builder/normalizeWorkdir: Always return cleaned path
The `normalizeWorkdir` function has two branches, one that returns a
result of `filepath.Join` which always returns a cleaned path, and
another one where the input string is returned unmodified.

To make these two outputs consistent, also clean the path in the second
branch.

This also makes the cleaning of the container workdir explicit in the
`normalizeWorkdir` function instead of relying on the
`SetupWorkingDirectory` to mutate it.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-19 12:42:19 +02:00
Paweł Gronowski
e829cca0ee Merge pull request #47584 from robmry/upstream_dns_windows
Windows DNS resolver forwarding
2024-04-19 11:34:50 +02:00
Sebastiaan van Stijn
82d8f8d6e6 Merge pull request from GHSA-x84c-p2g9-rqv9
Disable IPv6 for endpoints in '--ipv6=false' networks.
2024-04-18 17:50:35 +02:00
Rob Murray
6c68be24a2 Windows DNS resolver forwarding
Make the internal DNS resolver for Windows containers forward requests
to upsteam DNS servers when it cannot respond itself, rather than
returning SERVFAIL.

Windows containers are normally configured with the internal resolver
first for service discovery (container name lookup), then external
resolvers from '--dns' or the host's networking configuration.

When a tool like ping gets a SERVFAIL from the internal resolver, it
tries the other nameservers. But, nslookup does not, and with this
change it does not need to.

The internal resolver learns external server addresses from the
container's HNSEndpoint configuration, so it will use the same DNS
servers as processes in the container.

The internal resolver for Windows containers listens on the network's
gateway address, and each container may have a different set of external
DNS servers. So, the resolver uses the source address of the DNS request
to select external resolvers.

On Windows, daemon.json feature option 'windows-no-dns-proxy' can be used
to prevent the internal resolver from forwarding requests (restoring the
old behaviour).

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-04-16 18:57:28 +01:00
Sebastiaan van Stijn
b7c059886c Merge pull request #47706 from elezar/bump-container-device-interface
Update tags.cncf.io/container-device-interface to v0.7.1
2024-04-16 15:46:49 +02:00
Evan Lezar
745e2356ab Update tags.cncf.io/container-device-interface to v0.7.1
This also bumps the maximum supported CDI specification to v0.7.0.

Signed-off-by: Evan Lezar <elezar@nvidia.com>
2024-04-16 12:06:23 +02:00
Sebastiaan van Stijn
29f24a828b Merge pull request #47719 from thaJeztah/vendor_runtime_spec
vendor: github.com/opencontainers/runtime-spec v1.2.0
2024-04-16 11:50:50 +02:00
Sebastiaan van Stijn
0d6a1a212b vendor: github.com/opencontainers/runtime-spec v1.2.0
- deprecate Prestart hook
- deprecate kernel memory limits

Additions

- config: add idmap and ridmap mount options
- config.md: allow empty mappings for [r]idmap
- features-linux: Expose idmap information
- mount: Allow relative mount destinations on Linux
- features: add potentiallyUnsafeConfigAnnotations
- config: add support for org.opencontainers.image annotations

Minor fixes:

- config: improve bind mount and propagation doc

full diff: https://github.com/opencontainers/runtime-spec/compare/v1.1.0...v1.2.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-04-15 17:56:26 +02:00
Sebastiaan van Stijn
f5209d23a8 daemon: add nolint-comments for deprecated kernel-memory options, hooks
This adds some nolint-comments for the deprecated kernel-memory options; we
deprecated these, but they could technically still be accepted by alternative
runtimes.

    daemon/daemon_unix.go:108:3: 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. (staticcheck)
            memory.Kernel = &config.KernelMemory
            ^
    daemon/update_linux.go:63:3: 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. (staticcheck)
            memory.Kernel = &resources.KernelMemory
            ^

Prestart hooks are deprecated, and more granular hooks should be used instead.
CreateRuntime are 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).
As these hooks are still supported, this patch adds nolint comments, but adds
some TODOs to consider migrating to something else;

    daemon/nvidia_linux.go:86:2: SA1019: s.Hooks.Prestart is deprecated: use [Hooks.CreateRuntime], [Hooks.CreateContainer], and [Hooks.StartContainer] instead, which allow more granular hook control during the create and start phase. (staticcheck)
        s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{
        ^

    daemon/oci_linux.go:76:5: SA1019: s.Hooks.Prestart is deprecated: use [Hooks.CreateRuntime], [Hooks.CreateContainer], and [Hooks.StartContainer] instead, which allow more granular hook control during the create and start phase. (staticcheck)
                    s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{
                    ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-04-15 17:55:47 +02:00
Paweł Gronowski
442f29a699 Merge pull request #47711 from vvoland/swarm-subpath
daemon/cluster/executor: Add volume `Subpath`
2024-04-15 17:45:20 +02:00
Rob Murray
f07644e17e Add netiputil.AddrPortFromNet()
Co-authored-by: Cory Snider <csnider@mirantis.com>
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-04-15 14:51:20 +01:00
Paweł Gronowski
d3c051318f daemon/cluster/executor: Add volume Subpath
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-15 14:14:32 +02:00
Paweł Gronowski
5368c3a04f vendor: github.com/moby/swarmkit/v2 master (f3ffc0881d0e)
full diff: 911c97650f...f3ffc0881d

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-15 14:14:30 +02:00
Lei Jitang
8d5d655db0 Merge pull request #47708 from ViToni/fix_typos
Fix typo
2024-04-13 10:33:58 +08:00
Victor Toni
f51e18f58e Fix typo
Signed-off-by: Victor Toni <victor.toni@gmail.com>
2024-04-11 00:19:05 +02:00
Rob Murray
57dd56726a Disable IPv6 for endpoints in '--ipv6=false' networks.
No IPAM IPv6 address is given to an interface in a network with
'--ipv6=false', but the kernel would assign a link-local address and,
in a macvlan/ipvlan network, the interface may get a SLAAC-assigned
address.

So, disable IPv6 on the interface to avoid that.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-04-10 17:11:20 +01:00
Paweł Gronowski
f9dfd139ec Merge pull request #47657 from siepkes/illumos_fix
Minor fix for illumos support
2024-04-10 12:35:14 +02:00
Paweł Gronowski
051d587447 Merge pull request #47677 from robmry/47662_ipvlan_l3_dns
Enable external DNS for ipvlan-l3, and disable it for macvlan/ipvlan with no parent interface
2024-04-10 12:23:40 +02:00
Rob Murray
9954d7c6bd Run ipvlan tests even if 'modprobe ipvlan' fails
This reverts commit a77e147d32.

The ipvlan integration tests have been skipped in CI because of a check
intended to ensure the kernel has ipvlan support - which failed, but
seems to be unnecessary (probably because kernels have moved on).

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-04-10 08:52:08 +01:00
Rob Murray
cd7240f6d9 Stop macvlan with no parent from using ext-dns
We document that an macvlan network with no parent interface is
equivalent to a '--internal' network. But, in this case, an macvlan
network was still configured with a gateway. So, DNS proxying would
be enabled in the internal resolver (and, if the host's resolver
was on a localhost address, requests to external resolvers from the
host's network namespace would succeed).

This change disables configuration of a gateway for a macvlan Endpoint
if no parent interface is specified.

(Note if a parent interface with no external network is supplied as
'-o parent=<dummy>', the gateway will still be set up. Documentation
will need to be updated to note that '--internal' should be used to
prevent DNS request forwarding in this case.)

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-04-10 08:51:00 +01:00
Rob Murray
17b8631545 Enable DNS proxying for ipvlan-l3
The internal DNS resolver should only forward requests to external
resolvers if the libnetwork.Sandbox served by the resolver has external
network access (so, no forwarding for '--internal' networks).

The test for external network access was whether the Sandbox had an
Endpoint with a gateway configured.

However, an ipvlan-l3 networks with external network access does not
have a gateway, it has a default route bound to an interface.

Also, we document that an ipvlan network with no parent interface is
equivalent to a '--internal' network. But, in this case, an ipvlan-l2
network was configured with a gateway. So, DNS proxying would be enabled
in the internal resolver (and, if the host's resolver was on a localhost
address, requests to external resolvers from the host's network
namespace would succeed).

So, this change adjusts the test for enabling DNS proxying to include
a check for '--internal' (as a shortcut) and, for non-internal networks,
checks for a default route as well as a gateway. It also disables
configuration of a gateway or a default route for an ipvlan Endpoint if
no parent interface is specified.

(Note if a parent interface with no external network is supplied as
'-o parent=<dummy>', the gateway/default route will still be set up
and external DNS proxying will be enabled. The network must be
configured as '--internal' to prevent that from happening.)

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-04-10 08:50:57 +01:00
Sebastiaan van Stijn
8383c487c6 Merge pull request #47691 from vvoland/vendor-master-containerd-v1.7.15
vendor: github.com/containerd/containerd v1.7.15
2024-04-09 12:08:31 +02:00
Sebastiaan van Stijn
7a54a16740 Merge pull request #47647 from vvoland/ci-backport-title
github/ci: Check if backport is opened against the expected branch
2024-04-08 19:15:37 +02:00
Sebastiaan van Stijn
2fabb28813 Merge pull request #47689 from vvoland/update-containerd
update containerd binary to v1.7.15
2024-04-08 19:07:18 +02:00
Paweł Gronowski
5ae5969739 vendor: github.com/containerd/containerd v1.7.15
full diff: https://github.com/containerd/containerd/compare/v1.7.14...v1.7.15

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-08 14:33:32 +02:00
Paweł Gronowski
3485cfbb1e update containerd binary to v1.7.15
Update the containerd binary that's used in CI

- full diff: https://github.com/containerd/containerd/compare/v1.7.13...v1.7.15
- release notes: https://github.com/containerd/containerd/releases/tag/v1.7.15

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-08 13:01:28 +02:00
Tianon Gravi
3b81ca4969 Merge pull request #47661 from cpuguy83/oci_tar_no_platform
save: Remove platform from config descriptor
2024-04-05 15:38:30 -07:00
Sebastiaan van Stijn
80572929e1 Merge pull request #47682 from vvoland/ci-check-changelog-error
ci/validate-pr: Use `::error::` command to print errors
2024-04-05 23:05:00 +02:00
Paweł Gronowski
fb92caf2aa ci/validate-pr: Use ::error:: command to print errors
This will make Github render the log line as an error.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-05 14:56:50 +02:00
Paweł Gronowski
61269e718f github/ci: Check if backport is opened against the expected branch
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-05 11:55:14 +02:00
Sebastiaan van Stijn
d25b0bd7ea Merge pull request #47673 from thaJeztah/vendor_x_net
vendor: golang.org/x/net v0.23.0
2024-04-04 14:31:05 +02:00
Rob Murray
d8b768149b Move dummy DNS server to integration/internal/network
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-04-04 12:02:22 +01:00
Sebastiaan van Stijn
6d30487d2e Merge pull request #47670 from vvoland/update-go
update to go1.21.9
2024-04-04 12:11:14 +02:00
Paweł Gronowski
329d403e20 update to go1.21.9
go1.21.9 (released 2024-04-03) includes a security fix to the net/http
package, as well as bug fixes to the linker, and the go/types and
net/http packages. See the [Go 1.21.9 milestone](https://github.com/golang/go/issues?q=milestone%3AGo1.21.9+label%3ACherryPickApproved)
for more details.

These minor releases include 1 security fixes following the security policy:

- http2: close connections when receiving too many headers

Maintaining HPACK state requires that we parse and process all HEADERS
and CONTINUATION frames on a connection. When a request's headers exceed
MaxHeaderBytes, we don't allocate memory to store the excess headers but
we do parse them. This permits an attacker to cause an HTTP/2 endpoint
to read arbitrary amounts of header data, all associated with a request
which is going to be rejected. These headers can include Huffman-encoded
data which is significantly more expensive for the receiver to decode
than for an attacker to send.

Set a limit on the amount of excess header frames we will process before
closing a connection.

Thanks to Bartek Nowotarski (https://nowotarski.info/) for reporting this issue.

This is CVE-2023-45288 and Go issue https://go.dev/issue/65051.

View the release notes for more information:
https://go.dev/doc/devel/release#go1.22.2

- https://github.com/golang/go/issues?q=milestone%3AGo1.21.9+label%3ACherryPickApproved
- full diff: https://github.com/golang/go/compare/go1.21.8...go1.21.9

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-04-04 10:13:50 +02:00
Sebastiaan van Stijn
d66589496e vendor: golang.org/x/net v0.23.0
full diff: https://github.com/golang/net/compare/v0.22.0...v0.23.0

Includes a fix for CVE-2023-45288, which is also addressed in go1.22.2
and go1.21.9;

> http2: close connections when receiving too many headers
>
> Maintaining HPACK state requires that we parse and process
> all HEADERS and CONTINUATION frames on a connection.
> When a request's headers exceed MaxHeaderBytes, we don't
> allocate memory to store the excess headers but we do
> parse them. This permits an attacker to cause an HTTP/2
> endpoint to read arbitrary amounts of data, all associated
> with a request which is going to be rejected.
>
> Set a limit on the amount of excess header frames we
> will process before closing a connection.
>
> Thanks to Bartek Nowotarski for reporting this issue.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-04-03 20:42:29 +02:00
Sebastiaan van Stijn
e1ca74361b vendor: golang.org/x/net v0.22.0, golang.org/x/crypto v0.21.0
full diffs changes relevant to vendored code:

- https://github.com/golang/net/compare/v0.18.0...v0.22.0
    - websocket: add support for dialing with context
    - http2: remove suspicious uint32->v conversion in frame code
    - http2: send an error of FLOW_CONTROL_ERROR when exceed the maximum octets
- https://github.com/golang/crypto/compare/v0.17.0...v0.21.0
    - internal/poly1305: drop Go 1.12 compatibility
    - internal/poly1305: improve sum_ppc64le.s
    - ocsp: don't use iota for externally defined constants

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-04-03 20:38:05 +02:00
Jasper Siepkes
cf933115b6 Minor fix for illumos support
illumos is the opensource continuation of OpenSolaris after Oracle
closed to source it (again).

For example use see: https://github.com/openbao/openbao/pull/205.

Signed-off-by: Jasper Siepkes <siepkes@serviceplanet.nl>
2024-04-03 15:58:27 +02:00
Albin Kerouanton
9fa76786ab Merge pull request #47431 from akerouanton/api-normalize-default-NetworkMode
api: normalize the default NetworkMode
2024-04-03 15:44:24 +02:00
Brian Goff
9160b9fda6 save: Remove platform from config descriptor
This was brought up by bmitch that its not expected to have a platform
object in the config descriptor.
Also checked with tianon who agreed, its not _wrong_ but is unexpected
and doesn't neccessarily make sense to have it there.

Also, while technically incorrect, ECR is throwing an error when it sees
this.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2024-04-02 17:15:52 +00:00
Paweł Gronowski
8599f2a3fb Merge pull request #47658 from cpuguy83/fix_error_wrap_local_logs
Fix cases where we are wrapping a nil error
2024-04-02 10:28:09 +02:00
Brian Goff
0a48d26fbc Fix cases where we are wrapping a nil error
This was using `errors.Wrap` when there was no error to wrap, meanwhile
we are supposed to be creating a new error.

Found this while investigating some log corruption issues and
unexpectedly getting a nil reader and a nil error from `getTailReader`.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2024-04-01 21:30:43 +00:00
Bjorn Neergaard
330a6f959f Merge pull request #47645 from vvoland/community-slack
CONTRIBUTING.md: update Slack link
2024-03-28 14:25:58 -06:00
Albin Kerouanton
c4689034fd daemon: don't call NetworkMode.IsDefault()
Previous commit made this unnecessary.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2024-03-28 12:35:47 +01:00
Albin Kerouanton
4eed3dcdfe api: normalize the default NetworkMode
The NetworkMode "default" is now normalized into the value it
aliases ("bridge" on Linux and "nat" on Windows) by the
ContainerCreate endpoint, the legacy image builder, Swarm's
cluster executor and by the container restore codepath.

builder-next is left untouched as it already uses the normalized
value (ie. bridge).

Going forward, this will make maintenance easier as there's one
less NetworkMode to care about.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2024-03-28 12:34:23 +01:00
Paweł Gronowski
c187f95fe1 CONTRIBUTING.md: update Slack link
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-28 09:37:18 +01:00
Albin Kerouanton
a33b302d54 Merge pull request #47635 from robmry/backport-26.0/47619_restore_prestart_hook
[26.0 backport] Restore the SetKey prestart hook.
2024-03-28 08:24:48 +00:00
Paweł Gronowski
484480f56a Merge pull request #47636 from crazy-max/rm-artifacts-upload
ci: update workflow artifacts retention
2024-03-27 12:30:30 +01:00
CrazyMax
aff003139c ci: update workflow artifacts retention
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2024-03-27 10:57:01 +01:00
Rob Murray
1014f481de Restore the SetKey prestart hook.
Partially reverts 0046b16 "daemon: set libnetwork sandbox key w/o OCI hook"

Running SetKey to store the OCI Sandbox key after task creation, rather
than from the OCI prestart hook, meant it happened after sysctl settings
were applied by the runtime - which was the intention, we wanted to
complete Sandbox configuration after IPv6 had been disabled by a sysctl
if that was going to happen.

But, it meant '--sysctl' options for a specfic network interface caused
container task creation to fail, because the interface is only moved into
the network namespace during SetKey.

This change restores the SetKey prestart hook, and regenerates config
files that depend on the container's support for IPv6 after the task has
been created. It also adds a regression test that makes sure it's possible
to set an interface-specfic sysctl.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-03-27 08:50:25 +00:00
Albin Kerouanton
d57b899904 Merge pull request #47621 from robmry/47619_restore_prestart_hook
Restore the SetKey prestart hook.
2024-03-27 08:12:18 +00:00
Rob Murray
fde80fe2e7 Restore the SetKey prestart hook.
Partially reverts 0046b16 "daemon: set libnetwork sandbox key w/o OCI hook"

Running SetKey to store the OCI Sandbox key after task creation, rather
than from the OCI prestart hook, meant it happened after sysctl settings
were applied by the runtime - which was the intention, we wanted to
complete Sandbox configuration after IPv6 had been disabled by a sysctl
if that was going to happen.

But, it meant '--sysctl' options for a specfic network interface caused
container task creation to fail, because the interface is only moved into
the network namespace during SetKey.

This change restores the SetKey prestart hook, and regenerates config
files that depend on the container's support for IPv6 after the task has
been created. It also adds a regression test that makes sure it's possible
to set an interface-specfic sysctl.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-03-25 19:35:55 +00:00
Bjorn Neergaard
bfdb8918f9 Merge pull request #47613 from availhang/master
chore: fix mismatched function names in godoc
2024-03-22 15:48:57 -06:00
Sebastiaan van Stijn
83ae9927fb Merge pull request #47603 from neersighted/authors_mailmap
AUTHORS,.mailmap: update with recent contributors
2024-03-22 10:13:28 +01:00
George Ma
14a8fac092 chore: fix mismatched function names in godoc
Signed-off-by: George Ma <mayangang@outlook.com>
2024-03-22 16:24:31 +08:00
Brian Goff
59c5059081 Merge pull request #47443 from corhere/cnmallocator/lift-n-shift
Vendor dependency cycle-free swarmkit
2024-03-21 12:29:46 -07:00
Sebastiaan van Stijn
1552e30a05 Merge pull request #47595 from tonistiigi/dockerfile-dlv-update
Dockerfile: avoid hardcoding arch combinations for delve
2024-03-21 15:46:47 +01:00
Paweł Gronowski
c64314fd55 Merge pull request #47610 from vvoland/dockerfile-cli-v26
Dockerfile: update docker CLI to v26.0.0
2024-03-21 15:35:41 +01:00
Bjorn Neergaard
61e2199b78 AUTHORS,.mailmap: update with recent contributors
Signed-off-by: Bjorn Neergaard <bjorn.neergaard@docker.com>
2024-03-21 07:34:37 -06:00
Paweł Gronowski
ea72f9f72c Dockerfile: update docker CLI to v26.0.0
Update the CLI that's used in the dev-container

- full diff: https://github.com/docker/cli/compare/v26.0.0-rc2...v26.0.0

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-21 11:39:15 +01:00
Tonis Tiigi
f696e0d2a7 Dockerfile: avoid hardcoding arch combinations for delve
This is better because every possible platform combination
does not need to be defined in the Dockerfile. If built
for platform where Delve is not supported then it is just
skipped.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2024-03-19 10:22:35 -07:00
Cory Snider
7ebd88d2d9 hack: block imports of vendored testify packages
While github.com/stretchr/testify is not used directly by any of the
repository code, it is a transitive dependency via Swarmkit and
therefore still easy to use without having to revendor. Add lint rules
to ban importing testify packages to make sure nobody does.

Signed-off-by: Cory Snider <csnider@mirantis.com>
2024-02-29 16:20:15 -05:00
Cory Snider
4f30a930ad libn/cnmallocator: migrate tests to gotest.tools/v3
Apply command gotest.tools/v3/assert/cmd/gty-migrate-from-testify to the
cnmallocator package to be consistent with the assertion library used
elsewhere in moby.

Signed-off-by: Cory Snider <csnider@mirantis.com>
2024-02-29 16:14:02 -05:00
Cory Snider
7b0ab1011c Vendor dependency cycle-free swarmkit
Moby imports Swarmkit; Swarmkit no longer imports Moby. In order to
accomplish this feat, Swarmkit has introduced a new plugin.Getter
interface so it could stop importing our pkg/plugingetter package. This
new interface is not entirely compatible with our
plugingetter.PluginGetter interface, necessitating a thin adapter.

Swarmkit had to jettison the CNM network allocator to stop having to
import libnetwork as the cnmallocator package is deeply tied to
libnetwork. Move the CNM network allocator into libnetwork, where it
belongs. The package had a short an uninteresting Git history in the
Swarmkit repository so no effort was made to retain history.

Signed-off-by: Cory Snider <csnider@mirantis.com>
2024-02-28 09:46:45 -05:00
209 changed files with 32166 additions and 1206 deletions

View File

@@ -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.

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -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 {

View File

@@ -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

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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{

View 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
}

View File

@@ -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"))
}

View File

@@ -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
}

View File

@@ -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))

View File

@@ -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())),
}
}

View File

@@ -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))]

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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.
}
}

View File

@@ -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,

View File

@@ -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},
})

View File

@@ -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

View File

@@ -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"

View File

@@ -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,

View File

@@ -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"))
})
}
}

View File

@@ -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) {

View File

@@ -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))
})
}
}

View File

@@ -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})
})
}
}

View File

@@ -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:"))
}

View 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)
}

View File

@@ -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
}

View 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))
}

View File

@@ -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
}

View File

@@ -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 - ")

View File

@@ -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"},

View File

@@ -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"},
}

View File

@@ -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
}

View File

@@ -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
}

View 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)
}

View 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)
}

View 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)))
})
}
}

View File

@@ -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.

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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{}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View 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))
}
})
}
}

View File

@@ -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
}
}()

View File

@@ -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),

View File

@@ -92,6 +92,7 @@ type resolvConfPathConfig struct {
}
type containerConfig struct {
containerConfigOS //nolint:nolintlint,unused // only populated on windows
hostsPathConfig
resolvConfPathConfig
generic map[string]interface{}

View File

@@ -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.

View File

@@ -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.

View File

@@ -0,0 +1,7 @@
package libnetwork
func OptionDNSNoProxy() SandboxOption {
return func(sb *Sandbox) {
sb.config.dnsNoProxy = true
}
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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)
}
}
}

View 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
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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
)

View File

@@ -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=

View 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()
}

View 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()
}

View 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)
}
}

View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,159 @@
# bitset
*Go language library to map between non-negative integers and boolean values*
[![Test](https://github.com/bits-and-blooms/bitset/workflows/Test/badge.svg)](https://github.com/willf/bitset/actions?query=workflow%3ATest)
[![Go Report Card](https://goreportcard.com/badge/github.com/willf/bitset)](https://goreportcard.com/report/github.com/willf/bitset)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/bits-and-blooms/bitset?tab=doc)](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
View File

@@ -0,0 +1,5 @@
# Security Policy
## Reporting a Vulnerability
You can report privately a vulnerability by email at daniel@lemire.me (current maintainer).

View 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

File diff suppressed because it is too large Load Diff

53
vendor/github.com/bits-and-blooms/bitset/popcnt.go generated vendored Normal file
View 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
View 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)
}

View 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
View 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

View 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
View 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)
}

View 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])
}

View 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))
}

View File

@@ -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**
>

View File

@@ -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

View File

@@ -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

View File

@@ -104,6 +104,10 @@ const (
DeprecationsPlugin = "deprecations"
)
const (
SnapshotterRootDir = "root"
)
// Registration contains information for registering a plugin
type Registration struct {
// Type of the plugin

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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