Compare commits

...

15 Commits

Author SHA1 Message Date
Sebastiaan van Stijn
d05a725121 Merge pull request #51843 from corhere/backport-29.x/fix-service-binding-soft-disable
[docker-29.x backport] libnetwork: fix graceful service endpoint removal
2026-01-14 23:46:09 +01:00
Cory Snider
01fc4fcf8b libnetwork: fix graceful service endpoint removal
When a container is stopped, new connections to a published port will
not be routed to that container but any existing connections are
permitted to continue uninterrupted for the duration of the container's
grace period. Unfortunately recent fixes to overlay networks introduced
a regression: existing connections routed over the service mesh to
containers on remote nodes are dropped immediately when the container is
stopped, irrespective of the grace period.

Fix the handling of NetworkDB endpoint table events so that the endpoint
is disabled in the load balancer when a service endpoint transitions to
ServiceDisabled instead of deleting the endpoint and re-adding it. And
fix the other bugged state transitions with the help of a unit test
which exhaustively covers all permutations of endpoint event.

Signed-off-by: Cory Snider <csnider@mirantis.com>
(cherry picked from commit 9ec65542a0)
Signed-off-by: Cory Snider <csnider@mirantis.com>
2026-01-13 18:56:23 -05:00
Paweł Gronowski
08440b6ee8 Merge pull request #51830 from thaJeztah/29.x_backport_windows-network-none
[docker-29.x backport] daemon/libnetwork: Fix panic in findHNSEp when IP networks are nil
2026-01-08 16:57:22 +00:00
Paweł Gronowski
b0e62060b0 Merge pull request #51829 from thaJeztah/29.x_backport_fix-image-mount
[docker-29.x backport] daemon/volumes: More fs friendly image mount layer names
2026-01-08 16:57:12 +00:00
Sebastiaan van Stijn
515dbc8c71 Merge pull request #51826 from thaJeztah/29.x_backport_45939-init-layer-cleanup
[docker-29.x backport] layer: Clean up init layer if initialization fails
2026-01-08 17:24:06 +01:00
Sebastiaan van Stijn
adf3073cb6 Merge pull request #51825 from thaJeztah/29.x_backport_archive_rm_deprecated
[docker-29.x backport] remove uses of deprecated go-archive consts
2026-01-08 17:23:43 +01:00
Sebastiaan van Stijn
8b2c317218 Merge pull request #51824 from thaJeztah/29.x_backport_45939-rw-layer-cleanup
[docker-29.x backport] layer: Clean up RW layer if mount metadata save fails
2026-01-08 17:23:09 +01:00
Paweł Gronowski
3eca177282 daemon/libnetwork: Fix panic in findHNSEp when IP networks are nil
Can happen for `docker run --network none ...`

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit fadd8dc47c)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-01-08 16:59:22 +01:00
Paweł Gronowski
c4f4c6765e daemon/volumes: More fs friendly image mount layer names
Hash the container ID, mount source and destination together to form a
layer name.

This ensures the generated names are filesystem-friendly and don't
exceed path length limits while maintaining uniqueness across different
mount points and containers.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit cb88c6ba10)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-01-08 16:56:18 +01:00
Sebastiaan van Stijn
f942bce11a Merge pull request #51821 from vvoland/51740-docker-29.x
[docker-29.x backport] vendor: github.com/moby/buildkit v0.26.3
2026-01-08 16:41:14 +01:00
Paweł Gronowski
a1f7fff7a9 daemon/layer_store: Use named return error for defer
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit 26bb1af7e6)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-01-08 11:00:00 +01:00
Jan Scheffler
0e600c7fc4 layer: Clean up init layer if initialization fails
Add cleanup for the init layer directory if any operation fails after
driver.CreateReadWrite() succeeds in initMount(). Previously, failures
in driver.Get(), initFunc(), or driver.Put() would leave an orphaned
overlay2 directory.

Related to moby/moby#45939

Signed-off-by: Jan Scheffler <jan.scheffler@qodev.ai>
(cherry picked from commit 3fdde529e7)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-01-08 11:00:00 +01:00
Sebastiaan van Stijn
734bb626e4 remove uses of deprecated go-archive consts
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 7239c72eca)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-01-08 10:54:50 +01:00
Jan Scheffler
5eaae6db52 layer: Clean up RW layer if mount metadata save fails
Add cleanup for the RW layer directory if saveMount() fails after
driver.CreateReadWrite() succeeds. Previously, this failure path would
leave an orphaned overlay2 directory with no corresponding metadata.

Related to moby/moby#45939

Signed-off-by: Jan Scheffler <jan.scheffler@qodev.ai>
(cherry picked from commit d7a6250b91)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-01-08 10:49:11 +01:00
Jonathan A. Sternberg
8ebb104e36 vendor: github.com/moby/buildkit v0.26.3
Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
(cherry picked from commit c63bf203bf)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-01-07 16:44:38 +01:00
24 changed files with 474 additions and 92 deletions

View File

@@ -8,6 +8,7 @@ import (
"github.com/moby/go-archive"
"github.com/moby/go-archive/chrootarchive"
"github.com/moby/go-archive/compression"
containertypes "github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/events"
"github.com/moby/moby/v2/daemon/container"
@@ -278,7 +279,7 @@ func (daemon *Daemon) containerCopy(container *container.Container, resource str
filter = []string{f}
}
archv, err := chrootarchive.Tar(basePath, &archive.TarOptions{
Compression: archive.Uncompressed,
Compression: compression.None,
IncludeFiles: filter,
}, container.BaseFS)
if err != nil {

View File

@@ -7,6 +7,7 @@ import (
"github.com/moby/buildkit/frontend/dockerfile/instructions"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/v2/daemon/builder/remotecontext"
"github.com/moby/sys/reexec"
"gotest.tools/v3/assert"
@@ -105,7 +106,7 @@ func TestDispatch(t *testing.T) {
createTestTempFile(t, contextDir, filename, content, 0o777)
}
tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
tarStream, err := archive.Tar(contextDir, compression.None)
if err != nil {
t.Fatalf("Error when creating tar stream: %s", err)
}

View File

@@ -7,6 +7,7 @@ import (
"testing"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/v2/daemon/builder"
@@ -61,7 +62,7 @@ func readAndCheckDockerfile(t *testing.T, testName, contextDir, dockerfilePath,
if runtime.GOOS != "windows" {
skip.If(t, os.Getuid() != 0, "skipping test that requires root")
}
tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
tarStream, err := archive.Tar(contextDir, compression.None)
assert.NilError(t, err)
defer func() {

View File

@@ -6,6 +6,7 @@ import (
"github.com/containerd/log"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/v2/daemon/builder"
"github.com/moby/moby/v2/daemon/builder/remotecontext/git"
)
@@ -17,7 +18,7 @@ func MakeGitContext(gitURL string) (builder.Source, error) {
return nil, err
}
c, err := archive.Tar(root, archive.Uncompressed)
c, err := archive.Tar(root, compression.None)
if err != nil {
return nil, err
}

View File

@@ -6,6 +6,7 @@ import (
"testing"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/v2/daemon/builder"
"github.com/moby/sys/reexec"
"github.com/pkg/errors"
@@ -128,7 +129,7 @@ func TestRemoveDirectory(t *testing.T) {
func makeTestArchiveContext(t *testing.T, dir string) builder.Source {
skip.If(t, os.Getuid() != 0, "skipping test that requires root")
tarStream, err := archive.Tar(dir, archive.Uncompressed)
tarStream, err := archive.Tar(dir, compression.None)
if err != nil {
t.Fatalf("error: %s", err)
}

View File

@@ -15,6 +15,7 @@ import (
cerrdefs "github.com/containerd/errdefs"
"github.com/containerd/platforms"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/v2/daemon/server/imagebackend"
"github.com/moby/moby/v2/internal/testutil/labelstore"
"github.com/moby/moby/v2/internal/testutil/specialimage"
@@ -39,7 +40,7 @@ func TestImageLoad(t *testing.T) {
imgSvc.defaultPlatformOverride = platforms.Only(linuxAmd64)
tryLoad := func(ctx context.Context, t *testing.T, dir string, platformList []ocispec.Platform) error {
tarRc, err := archive.Tar(dir, archive.Uncompressed)
tarRc, err := archive.Tar(dir, compression.None)
assert.NilError(t, err)
defer tarRc.Close()

View File

@@ -9,6 +9,7 @@ import (
"github.com/containerd/log"
"github.com/moby/go-archive"
"github.com/moby/go-archive/chrootarchive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/api/types/events"
"github.com/moby/moby/v2/daemon/container"
"github.com/moby/moby/v2/errdefs"
@@ -65,7 +66,7 @@ func (daemon *Daemon) containerExport(ctx context.Context, ctr *container.Contai
}()
archv, err := chrootarchive.Tar(basefs, &archive.TarOptions{
Compression: archive.Uncompressed,
Compression: compression.None,
IDMap: daemon.idMapping,
}, basefs)
if err != nil {

View File

@@ -8,6 +8,7 @@ import (
"github.com/containerd/log"
"github.com/moby/go-archive"
"github.com/moby/go-archive/chrootarchive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/v2/pkg/ioutils"
"github.com/moby/sys/user"
)
@@ -64,7 +65,7 @@ func (gdw *NaiveDiffDriver) Diff(id, parent string) (arch io.ReadCloser, retErr
}()
if parent == "" {
tarArchive, err := archive.Tar(layerFs, archive.Uncompressed)
tarArchive, err := archive.Tar(layerFs, compression.None)
if err != nil {
return nil, err
}

View File

@@ -19,6 +19,7 @@ import (
"github.com/docker/go-units"
"github.com/moby/go-archive"
"github.com/moby/go-archive/chrootarchive"
"github.com/moby/go-archive/compression"
"github.com/moby/locker"
"github.com/moby/moby/v2/daemon/graphdriver"
"github.com/moby/moby/v2/daemon/graphdriver/overlayutils"
@@ -721,7 +722,7 @@ func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) {
diffPath := d.getDiffPath(id)
logger.Debugf("Tar with options on %s", diffPath)
return archive.TarWithOptions(diffPath, &archive.TarOptions{
Compression: archive.Uncompressed,
Compression: compression.None,
IDMap: d.idMap,
WhiteoutFormat: archive.OverlayWhiteoutFormat,
})

View File

@@ -17,6 +17,7 @@ import (
"github.com/distribution/reference"
"github.com/docker/distribution"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/api/types/events"
"github.com/moby/moby/v2/daemon/internal/image"
v1 "github.com/moby/moby/v2/daemon/internal/image/v1"
@@ -395,7 +396,7 @@ func (s *saveSession) writeTar(ctx context.Context, tempDir string, outStream io
ctx, span := tracing.StartSpan(ctx, "writeTar")
defer span.End()
fs, err := archive.Tar(tempDir, archive.Uncompressed)
fs, err := archive.Tar(tempDir, compression.None)
if err != nil {
span.SetStatus(err)
return err

View File

@@ -535,6 +535,9 @@ func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWL
return nil, err
}
if err := ls.saveMount(m); err != nil {
if removeErr := ls.driver.Remove(m.mountID); removeErr != nil {
log.G(context.TODO()).WithFields(log.Fields{"mount-id": m.mountID, "error": removeErr}).Error("Failed to clean up RW layer after mount save failure")
}
return nil, err
}
@@ -645,7 +648,7 @@ func (ls *layerStore) saveMount(mount *mountedLayer) error {
return nil
}
func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc MountInit, storageOpt map[string]string) (string, error) {
func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc MountInit, storageOpt map[string]string) (_ string, retErr error) {
// Use "<graph-id>-init" to maintain compatibility with graph drivers
// which are expecting this layer with this special name. If all
// graph drivers can be updated to not rely on knowing about this layer
@@ -660,6 +663,16 @@ func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc Mou
if err := ls.driver.CreateReadWrite(initID, parent, createOpts); err != nil {
return "", err
}
// Clean up init layer if any subsequent operation fails
defer func() {
if retErr != nil {
if err := ls.driver.Remove(initID); err != nil {
log.G(context.TODO()).WithFields(log.Fields{"init-id": initID, "error": err}).Error("Failed to clean up init layer after error")
}
}
}()
p, err := ls.driver.Get(initID, "")
if err != nil {
return "", err

View File

@@ -12,6 +12,7 @@ import (
"github.com/containerd/continuity/driver"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/v2/daemon/graphdriver"
"github.com/moby/moby/v2/daemon/graphdriver/vfs"
"github.com/moby/moby/v2/daemon/internal/stringid"
@@ -587,7 +588,7 @@ func tarFromFiles(files ...FileApplier) ([]byte, error) {
}
}
r, err := archive.Tar(td, archive.Uncompressed)
r, err := archive.Tar(td, compression.None)
if err != nil {
return nil, err
}

View File

@@ -362,7 +362,7 @@ func (c *Controller) agentInit(listenAddr, bindAddrOrInterface, advertiseAddr, d
}
c.mu.Unlock()
go c.handleTableEvents(ch, c.handleEpTableEvent)
go c.handleTableEvents(ch, func(ev events.Event) { handleEpTableEvent(c, ev) })
go c.handleTableEvents(nodeCh, c.handleNodeTableEvent)
keys, tags := c.getKeys(subsysIPSec)
@@ -894,14 +894,17 @@ func unmarshalEndpointRecord(data []byte) (*endpointEvent, error) {
}, nil
}
// EquivalentTo returns true if ev is semantically equivalent to other.
// EquivalentTo returns true if ev is semantically equivalent to other,
// ignoring the ServiceDisabled field.
func (ev *endpointEvent) EquivalentTo(other *endpointEvent) bool {
if ev == nil || other == nil {
return (ev == nil) == (other == nil)
}
return ev.Name == other.Name &&
ev.ServiceName == other.ServiceName &&
ev.ServiceID == other.ServiceID &&
ev.VirtualIP == other.VirtualIP &&
ev.EndpointIP == other.EndpointIP &&
ev.ServiceDisabled == other.ServiceDisabled &&
iterutil.SameValues(
iterutil.Deref(slices.Values(ev.IngressPorts)),
iterutil.Deref(slices.Values(other.IngressPorts))) &&
@@ -909,7 +912,14 @@ func (ev *endpointEvent) EquivalentTo(other *endpointEvent) bool {
iterutil.SameValues(slices.Values(ev.TaskAliases), slices.Values(other.TaskAliases))
}
func (c *Controller) handleEpTableEvent(ev events.Event) {
type serviceBinder interface {
addContainerNameResolution(nID, eID, containerName string, taskAliases []string, ip net.IP, method string) error
delContainerNameResolution(nID, eID, containerName string, taskAliases []string, ip net.IP, method string) error
addServiceBinding(svcName, svcID, nID, eID, containerName string, vip net.IP, ingressPorts []*PortConfig, serviceAliases, taskAliases []string, ip net.IP, method string) error
rmServiceBinding(svcName, svcID, nID, eID, containerName string, vip net.IP, ingressPorts []*PortConfig, serviceAliases []string, taskAliases []string, ip net.IP, method string, deleteSvcRecords bool, fullRemove bool) error
}
func handleEpTableEvent(c serviceBinder, ev events.Event) {
event := ev.(networkdb.WatchEvent)
nid := event.NetworkID
eid := event.Key
@@ -939,8 +949,9 @@ func (c *Controller) handleEpTableEvent(ev events.Event) {
})
logger.Debug("handleEpTableEvent")
equivalent := prev.EquivalentTo(epRec)
if prev != nil {
if epRec != nil && prev.EquivalentTo(epRec) {
if equivalent && prev.ServiceDisabled == epRec.ServiceDisabled {
// Avoid flapping if we would otherwise remove a service
// binding then immediately replace it with an equivalent one.
return
@@ -948,7 +959,11 @@ func (c *Controller) handleEpTableEvent(ev events.Event) {
if prev.ServiceID != "" {
// This is a remote task part of a service
if !prev.ServiceDisabled {
if epRec == nil || !equivalent {
// Either the endpoint is deleted from NetworkDB or has
// been replaced with a different one. Remove the old
// binding. The new binding, if any, will be added
// below.
err := c.rmServiceBinding(prev.ServiceName, prev.ServiceID, nid, eid,
prev.Name, prev.VirtualIP.AsSlice(), prev.IngressPorts,
prev.Aliases, prev.TaskAliases, prev.EndpointIP.AsSlice(),
@@ -957,7 +972,7 @@ func (c *Controller) handleEpTableEvent(ev events.Event) {
logger.WithError(err).Error("failed removing service binding")
}
}
} else {
} else if !prev.ServiceDisabled && (!equivalent || epRec == nil || epRec.ServiceDisabled) {
// This is a remote container simply attached to an attachable network
err := c.delContainerNameResolution(nid, eid, prev.Name, prev.TaskAliases,
prev.EndpointIP.AsSlice(), "handleEpTableEvent")
@@ -970,19 +985,16 @@ func (c *Controller) handleEpTableEvent(ev events.Event) {
if epRec != nil {
if epRec.ServiceID != "" {
// This is a remote task part of a service
if epRec.ServiceDisabled {
// Don't double-remove a service binding
if prev == nil || prev.ServiceID != epRec.ServiceID || !prev.ServiceDisabled {
err := c.rmServiceBinding(epRec.ServiceName, epRec.ServiceID,
nid, eid, epRec.Name, epRec.VirtualIP.AsSlice(),
epRec.IngressPorts, epRec.Aliases, epRec.TaskAliases,
epRec.EndpointIP.AsSlice(), "handleEpTableEvent", true, false)
if err != nil {
logger.WithError(err).Error("failed disabling service binding")
return
}
if equivalent && !prev.ServiceDisabled && epRec.ServiceDisabled {
err := c.rmServiceBinding(epRec.ServiceName, epRec.ServiceID,
nid, eid, epRec.Name, epRec.VirtualIP.AsSlice(),
epRec.IngressPorts, epRec.Aliases, epRec.TaskAliases,
epRec.EndpointIP.AsSlice(), "handleEpTableEvent", true, false)
if err != nil {
logger.WithError(err).Error("failed disabling service binding")
return
}
} else {
} else if !epRec.ServiceDisabled {
err := c.addServiceBinding(epRec.ServiceName, epRec.ServiceID, nid, eid,
epRec.Name, epRec.VirtualIP.AsSlice(), epRec.IngressPorts,
epRec.Aliases, epRec.TaskAliases, epRec.EndpointIP.AsSlice(),
@@ -992,7 +1004,7 @@ func (c *Controller) handleEpTableEvent(ev events.Event) {
return
}
}
} else {
} else if !epRec.ServiceDisabled && (!equivalent || prev == nil || prev.ServiceDisabled) {
// This is a remote container simply attached to an attachable network
err := c.addContainerNameResolution(nid, eid, epRec.Name, epRec.TaskAliases,
epRec.EndpointIP.AsSlice(), "handleEpTableEvent")

View File

@@ -1,11 +1,16 @@
package libnetwork
import (
"fmt"
"net"
"net/netip"
"slices"
"testing"
"github.com/gogo/protobuf/proto"
"gotest.tools/v3/assert"
"github.com/moby/moby/v2/daemon/libnetwork/networkdb"
)
func TestEndpointEvent_EquivalentTo(t *testing.T) {
@@ -40,9 +45,12 @@ func TestEndpointEvent_EquivalentTo(t *testing.T) {
return a.EquivalentTo(b)
}
assert.Check(t, reflexiveEquiv(nil, nil), "nil should be equivalent to nil")
assert.Check(t, !reflexiveEquiv(&a, nil), "non-nil should not be equivalent to nil")
b := a
b.ServiceDisabled = true
assert.Check(t, !reflexiveEquiv(&a, &b), "differing by ServiceDisabled")
assert.Check(t, reflexiveEquiv(&a, &b), "ServiceDisabled value should not matter")
c := a
c.IngressPorts = slices.Clone(a.IngressPorts)
@@ -88,3 +96,321 @@ func TestEndpointEvent_EquivalentTo(t *testing.T) {
l.Name = "aaaaa"
assert.Check(t, !reflexiveEquiv(&a, &l), "Differing Name should not be equivalent")
}
type mockServiceBinder struct {
actions []string
}
func (m *mockServiceBinder) addContainerNameResolution(nID, eID, containerName string, _ []string, ip net.IP, _ string) error {
m.actions = append(m.actions, fmt.Sprintf("addContainerNameResolution(%v, %v, %v, %v)", nID, eID, containerName, ip))
return nil
}
func (m *mockServiceBinder) delContainerNameResolution(nID, eID, containerName string, _ []string, ip net.IP, _ string) error {
m.actions = append(m.actions, fmt.Sprintf("delContainerNameResolution(%v, %v, %v, %v)", nID, eID, containerName, ip))
return nil
}
func (m *mockServiceBinder) addServiceBinding(svcName, svcID, nID, eID, containerName string, vip net.IP, _ []*PortConfig, _, _ []string, ip net.IP, _ string) error {
m.actions = append(m.actions, fmt.Sprintf("addServiceBinding(%v, %v, %v, %v, %v, %v, %v)", svcName, svcID, nID, eID, containerName, vip, ip))
return nil
}
func (m *mockServiceBinder) rmServiceBinding(svcName, svcID, nID, eID, containerName string, vip net.IP, _ []*PortConfig, _, _ []string, ip net.IP, _ string, deleteSvcRecords bool, fullRemove bool) error {
m.actions = append(m.actions, fmt.Sprintf("rmServiceBinding(%v, %v, %v, %v, %v, %v, %v, deleteSvcRecords=%v, fullRemove=%v)", svcName, svcID, nID, eID, containerName, vip, ip, deleteSvcRecords, fullRemove))
return nil
}
func TestHandleEPTableEvent(t *testing.T) {
svc1 := EndpointRecord{
Name: "ep1",
ServiceName: "svc1",
ServiceID: "id1",
VirtualIP: "10.0.0.1",
EndpointIP: "192.168.12.42",
}
svc1disabled := svc1
svc1disabled.ServiceDisabled = true
svc2 := EndpointRecord{
Name: "ep2",
ServiceName: "svc2",
ServiceID: "id2",
VirtualIP: "10.0.0.2",
EndpointIP: "172.16.69.5",
}
svc2disabled := svc2
svc2disabled.ServiceDisabled = true
ctr1 := EndpointRecord{
Name: "ctr1",
EndpointIP: "172.18.1.1",
}
ctr1disabled := ctr1
ctr1disabled.ServiceDisabled = true
ctr2 := EndpointRecord{
Name: "ctr2",
EndpointIP: "172.18.1.2",
}
ctr2disabled := ctr2
ctr2disabled.ServiceDisabled = true
tests := []struct {
name string
prev, ev *EndpointRecord
expectedActions []string
}{
{
name: "Insert/Service/ServiceDisabled=false",
ev: &svc1,
expectedActions: []string{
"addServiceBinding(svc1, id1, network1, endpoint1, ep1, 10.0.0.1, 192.168.12.42)",
},
},
{
name: "Insert/Service/ServiceDisabled=true",
ev: &svc1disabled,
},
{
name: "Insert/Container/ServiceDisabled=false",
ev: &ctr1,
expectedActions: []string{
"addContainerNameResolution(network1, endpoint1, ctr1, 172.18.1.1)",
},
},
{
name: "Insert/Container/ServiceDisabled=true",
ev: &ctr1disabled,
},
{
name: "Update/Service/ServiceDisabled=ft",
prev: &svc1,
ev: &svc1disabled,
expectedActions: []string{
"rmServiceBinding(svc1, id1, network1, endpoint1, ep1, 10.0.0.1, 192.168.12.42, deleteSvcRecords=true, fullRemove=false)",
},
},
{
name: "Update/Service/ServiceDisabled=tf",
prev: &svc1disabled,
ev: &svc1,
expectedActions: []string{
"addServiceBinding(svc1, id1, network1, endpoint1, ep1, 10.0.0.1, 192.168.12.42)",
},
},
{
name: "Update/Service/ServiceDisabled=ff",
prev: &svc1disabled,
ev: &svc1disabled,
},
{
name: "Update/Service/ServiceDisabled=tt",
prev: &svc1,
ev: &svc1,
},
{
name: "Update/Container/ServiceDisabled=ft",
prev: &ctr1,
ev: &ctr1disabled,
expectedActions: []string{
"delContainerNameResolution(network1, endpoint1, ctr1, 172.18.1.1)",
},
},
{
name: "Update/Container/ServiceDisabled=tf",
prev: &ctr1disabled,
ev: &ctr1,
expectedActions: []string{
"addContainerNameResolution(network1, endpoint1, ctr1, 172.18.1.1)",
},
},
{
name: "Update/Container/ServiceDisabled=ff",
prev: &ctr1disabled,
ev: &ctr1disabled,
},
{
name: "Update/Container/ServiceDisabled=tt",
prev: &ctr1,
ev: &ctr1,
},
{
name: "Delete/Service/ServiceDisabled=false",
prev: &svc1,
expectedActions: []string{
"rmServiceBinding(svc1, id1, network1, endpoint1, ep1, 10.0.0.1, 192.168.12.42, deleteSvcRecords=true, fullRemove=true)",
},
},
{
name: "Delete/Service/ServiceDisabled=true",
prev: &svc1disabled,
expectedActions: []string{
"rmServiceBinding(svc1, id1, network1, endpoint1, ep1, 10.0.0.1, 192.168.12.42, deleteSvcRecords=true, fullRemove=true)",
},
},
{
name: "Delete/Container/ServiceDisabled=false",
prev: &ctr1,
expectedActions: []string{
"delContainerNameResolution(network1, endpoint1, ctr1, 172.18.1.1)",
},
},
{
name: "Delete/Container/ServiceDisabled=true",
prev: &ctr1disabled,
},
{
name: "Replace/From=Service/To=Service/ServiceDisabled=ff",
prev: &svc1,
ev: &svc2,
expectedActions: []string{
"rmServiceBinding(svc1, id1, network1, endpoint1, ep1, 10.0.0.1, 192.168.12.42, deleteSvcRecords=true, fullRemove=true)",
"addServiceBinding(svc2, id2, network1, endpoint1, ep2, 10.0.0.2, 172.16.69.5)",
},
},
{
name: "Replace/From=Service/To=Service/ServiceDisabled=ft",
prev: &svc1,
ev: &svc2disabled,
expectedActions: []string{
"rmServiceBinding(svc1, id1, network1, endpoint1, ep1, 10.0.0.1, 192.168.12.42, deleteSvcRecords=true, fullRemove=true)",
},
},
{
name: "Replace/From=Service/To=Service/ServiceDisabled=tf",
prev: &svc1disabled,
ev: &svc2,
expectedActions: []string{
"rmServiceBinding(svc1, id1, network1, endpoint1, ep1, 10.0.0.1, 192.168.12.42, deleteSvcRecords=true, fullRemove=true)",
"addServiceBinding(svc2, id2, network1, endpoint1, ep2, 10.0.0.2, 172.16.69.5)",
},
},
{
name: "Replace/From=Service/To=Service/ServiceDisabled=tt",
prev: &svc1disabled,
ev: &svc2disabled,
expectedActions: []string{
"rmServiceBinding(svc1, id1, network1, endpoint1, ep1, 10.0.0.1, 192.168.12.42, deleteSvcRecords=true, fullRemove=true)",
},
},
{
name: "Replace/From=Service/To=Container/ServiceDisabled=ff",
prev: &svc1,
ev: &ctr2,
expectedActions: []string{
"rmServiceBinding(svc1, id1, network1, endpoint1, ep1, 10.0.0.1, 192.168.12.42, deleteSvcRecords=true, fullRemove=true)",
"addContainerNameResolution(network1, endpoint1, ctr2, 172.18.1.2)",
},
},
{
name: "Replace/From=Service/To=Container/ServiceDisabled=ft",
prev: &svc1,
ev: &ctr2disabled,
expectedActions: []string{
"rmServiceBinding(svc1, id1, network1, endpoint1, ep1, 10.0.0.1, 192.168.12.42, deleteSvcRecords=true, fullRemove=true)",
},
},
{
name: "Replace/From=Service/To=Container/ServiceDisabled=tf",
prev: &svc1disabled,
ev: &ctr2,
expectedActions: []string{
"rmServiceBinding(svc1, id1, network1, endpoint1, ep1, 10.0.0.1, 192.168.12.42, deleteSvcRecords=true, fullRemove=true)",
"addContainerNameResolution(network1, endpoint1, ctr2, 172.18.1.2)",
},
},
{
name: "Replace/From=Service/To=Container/ServiceDisabled=tt",
prev: &svc1disabled,
ev: &ctr2disabled,
expectedActions: []string{
"rmServiceBinding(svc1, id1, network1, endpoint1, ep1, 10.0.0.1, 192.168.12.42, deleteSvcRecords=true, fullRemove=true)",
},
},
{
name: "Replace/From=Container/To=Service/ServiceDisabled=ff",
prev: &ctr1,
ev: &svc2,
expectedActions: []string{
"delContainerNameResolution(network1, endpoint1, ctr1, 172.18.1.1)",
"addServiceBinding(svc2, id2, network1, endpoint1, ep2, 10.0.0.2, 172.16.69.5)",
},
},
{
name: "Replace/From=Container/To=Service/ServiceDisabled=ft",
prev: &ctr1,
ev: &svc2disabled,
expectedActions: []string{
"delContainerNameResolution(network1, endpoint1, ctr1, 172.18.1.1)",
},
},
{
name: "Replace/From=Container/To=Service/ServiceDisabled=tf",
prev: &ctr1disabled,
ev: &svc2,
expectedActions: []string{
"addServiceBinding(svc2, id2, network1, endpoint1, ep2, 10.0.0.2, 172.16.69.5)",
},
},
{
name: "Replace/From=Container/To=Service/ServiceDisabled=tt",
prev: &ctr1disabled,
ev: &svc2disabled,
},
{
name: "Replace/From=Container/To=Container/ServiceDisabled=ff",
prev: &ctr1,
ev: &ctr2,
expectedActions: []string{
"delContainerNameResolution(network1, endpoint1, ctr1, 172.18.1.1)",
"addContainerNameResolution(network1, endpoint1, ctr2, 172.18.1.2)",
},
},
{
name: "Replace/From=Container/To=Container/ServiceDisabled=ft",
prev: &ctr1,
ev: &ctr2disabled,
expectedActions: []string{
"delContainerNameResolution(network1, endpoint1, ctr1, 172.18.1.1)",
},
},
{
name: "Replace/From=Container/To=Container/ServiceDisabled=tf",
prev: &ctr1disabled,
ev: &ctr2,
expectedActions: []string{
"addContainerNameResolution(network1, endpoint1, ctr2, 172.18.1.2)",
},
},
{
name: "Replace/From=Container/To=Container/ServiceDisabled=tt",
prev: &ctr1disabled,
ev: &ctr2disabled,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
msb := &mockServiceBinder{}
event := networkdb.WatchEvent{
NetworkID: "network1",
Key: "endpoint1",
}
var err error
if tt.prev != nil {
event.Prev, err = proto.Marshal(tt.prev)
assert.NilError(t, err)
}
if tt.ev != nil {
event.Value, err = proto.Marshal(tt.ev)
assert.NilError(t, err)
}
handleEpTableEvent(msb, event)
assert.DeepEqual(t, tt.expectedActions, msb.actions)
})
}
}

View File

@@ -251,9 +251,14 @@ func deleteEpFromResolverImpl(
}
func findHNSEp(ip4, ip6 *net.IPNet, hnsEndpoints []hcsshim.HNSEndpoint) *hcsshim.HNSEndpoint {
if ip4 == nil && ip6 == nil {
return nil
}
for _, hnsEp := range hnsEndpoints {
if (hnsEp.IPAddress != nil && hnsEp.IPAddress.Equal(ip4.IP)) ||
(hnsEp.IPv6Address != nil && hnsEp.IPv6Address.Equal(ip6.IP)) {
if ip4 != nil && hnsEp.IPAddress != nil && hnsEp.IPAddress.Equal(ip4.IP) {
return &hnsEp
}
if ip6 != nil && hnsEp.IPv6Address != nil && hnsEp.IPv6Address.Equal(ip6.IP) {
return &hnsEp
}
}

View File

@@ -2,6 +2,7 @@ package daemon
import (
"context"
"crypto/sha256"
"encoding/hex"
"os"
"path/filepath"
@@ -259,10 +260,11 @@ func (daemon *Daemon) registerMountPoints(ctr *container.Container, defaultReadO
StorageOpt: ctr.HostConfig.StorageOpt,
}
// Include the destination in the layer name to make it unique for each mount point and container.
// Hash the source and destination to create a safe, unique identifier for each mount point and container.
// This makes sure that the same image can be mounted multiple times with different destinations.
// Hex encode the destination to create a safe, unique identifier
layerName := hex.EncodeToString([]byte(ctr.ID + ",src=" + mp.Source + ",dst=" + mp.Destination))
// We hash it so that the snapshot name is friendly to the underlying filesystem and doesn't exceed path length limits.
destHash := sha256.Sum256([]byte(ctr.ID + "-src=" + mp.Source + "-dst=" + mp.Destination))
layerName := hex.EncodeToString(destHash[:])
layer, err := daemon.imageService.CreateLayerFromImage(img, layerName, rwLayerOpts)
if err != nil {
return err

2
go.mod
View File

@@ -56,7 +56,7 @@ require (
github.com/miekg/dns v1.1.66
github.com/mistifyio/go-zfs/v3 v3.1.0
github.com/mitchellh/copystructure v1.2.0
github.com/moby/buildkit v0.26.2
github.com/moby/buildkit v0.26.3
github.com/moby/docker-image-spec v1.3.1
github.com/moby/go-archive v0.1.0
github.com/moby/ipvs v1.1.0

4
go.sum
View File

@@ -417,8 +417,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/buildkit v0.26.2 h1:EIh5j0gzRsCZmQzvgNNWzSDbuKqwUIiBH7ssqLv8RU8=
github.com/moby/buildkit v0.26.2/go.mod h1:ylDa7IqzVJgLdi/wO7H1qLREFQpmhFbw2fbn4yoTw40=
github.com/moby/buildkit v0.26.3 h1:D+ruZVAk/3ipRq5XRxBH9/DIFpRjSlTtMbghT5gQP9g=
github.com/moby/buildkit v0.26.3/go.mod h1:4T4wJzQS4kYWIfFRjsbJry4QoxDBjK+UGOEOs1izL7w=
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/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=

View File

@@ -2024,11 +2024,11 @@ CMD ["cat", "/foo"]`),
}
func (s *DockerCLIBuildSuite) TestBuildContextTarGzip(c *testing.T) {
testContextTar(c, archive.Gzip)
testContextTar(c, compression.Gzip)
}
func (s *DockerCLIBuildSuite) TestBuildContextTarNoCompression(c *testing.T) {
testContextTar(c, archive.Uncompressed)
testContextTar(c, compression.None)
}
func (s *DockerCLIBuildSuite) TestBuildNoContext(c *testing.T) {

View File

@@ -10,6 +10,7 @@ import (
"time"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/api/types/events"
plugintypes "github.com/moby/moby/api/types/plugin"
"github.com/moby/moby/api/types/registry"
@@ -209,7 +210,7 @@ func makePluginBundle(inPath string, opts ...CreateOpt) (io.ReadCloser, error) {
if err := archive.NewDefaultArchiver().CopyFileWithTar(cfg.binPath, filepath.Join(inPath, "rootfs", p.Entrypoint[0])); err != nil {
return nil, errors.Wrap(err, "error copying plugin binary to rootfs path")
}
tar, err := archive.Tar(inPath, archive.Uncompressed)
tar, err := archive.Tar(inPath, compression.None)
return tar, errors.Wrap(err, "error making plugin archive")
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/distribution/reference"
"github.com/google/uuid"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -137,7 +138,7 @@ func fileArchive(dir string, name string, content []byte) (io.ReadCloser, error)
return nil, err
}
return archive.Tar(tmp, archive.Uncompressed)
return archive.Tar(tmp, compression.None)
}
func writeLayerWithOneFile(dir string, filename string, content []byte) (ocispec.Descriptor, error) {

View File

@@ -653,43 +653,7 @@ func (lbf *llbBridgeForwarder) ResolveSourceMeta(ctx context.Context, req *pb.Re
if err != nil {
return nil, err
}
r := &pb.ResolveSourceMetaResponse{
Source: resp.Op,
}
if resp.Image != nil {
r.Image = &pb.ResolveSourceImageResponse{
Digest: string(resp.Image.Digest),
Config: resp.Image.Config,
}
if resp.Image.AttestationChain != nil {
r.Image.AttestationChain = toPBAttestationChain(resp.Image.AttestationChain)
}
}
if resp.Git != nil {
r.Git = &pb.ResolveSourceGitResponse{
Checksum: resp.Git.Checksum,
Ref: resp.Git.Ref,
CommitChecksum: resp.Git.CommitChecksum,
CommitObject: resp.Git.CommitObject,
TagObject: resp.Git.TagObject,
}
}
if resp.HTTP != nil {
var lastModified *timestamp.Timestamp
if resp.HTTP.LastModified != nil {
lastModified = &timestamp.Timestamp{
Seconds: resp.HTTP.LastModified.Unix(),
}
}
r.HTTP = &pb.ResolveSourceHTTPResponse{
Checksum: resp.HTTP.Digest.String(),
Filename: resp.HTTP.Filename,
LastModified: lastModified,
}
}
return r, nil
return ToPBResolveSourceMetaResponse(resp), nil
}
func (lbf *llbBridgeForwarder) ResolveImageConfig(ctx context.Context, req *pb.ResolveImageConfigRequest) (*pb.ResolveImageConfigResponse, error) {
@@ -1705,6 +1669,45 @@ func getCaps(label string) map[string]struct{} {
return out
}
func ToPBResolveSourceMetaResponse(in *sourceresolver.MetaResponse) *pb.ResolveSourceMetaResponse {
r := &pb.ResolveSourceMetaResponse{
Source: in.Op,
}
if in.Image != nil {
r.Image = &pb.ResolveSourceImageResponse{
Digest: string(in.Image.Digest),
Config: in.Image.Config,
}
if in.Image.AttestationChain != nil {
r.Image.AttestationChain = toPBAttestationChain(in.Image.AttestationChain)
}
}
if in.Git != nil {
r.Git = &pb.ResolveSourceGitResponse{
Checksum: in.Git.Checksum,
Ref: in.Git.Ref,
CommitChecksum: in.Git.CommitChecksum,
CommitObject: in.Git.CommitObject,
TagObject: in.Git.TagObject,
}
}
if in.HTTP != nil {
var lastModified *timestamp.Timestamp
if in.HTTP.LastModified != nil {
lastModified = &timestamp.Timestamp{
Seconds: in.HTTP.LastModified.Unix(),
}
}
r.HTTP = &pb.ResolveSourceHTTPResponse{
Checksum: in.HTTP.Digest.String(),
Filename: in.HTTP.Filename,
LastModified: lastModified,
}
}
return r
}
func toPBAttestationChain(ac *sourceresolver.AttestationChain) *pb.AttestationChain {
if ac == nil {
return nil

View File

@@ -5,6 +5,7 @@ import (
"strings"
"github.com/moby/buildkit/client/llb/sourceresolver"
"github.com/moby/buildkit/frontend/gateway"
gatewaypb "github.com/moby/buildkit/frontend/gateway/pb"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/sourcepolicy"
@@ -88,19 +89,26 @@ func (p *policyEvaluator) Evaluate(ctx context.Context, op *pb.Op) (bool, error)
Platform: toOCIPlatform(metareq.Platform),
}
}
if metareq.Image != nil {
if op.ImageOpt == nil {
op.ImageOpt = &sourceresolver.ResolveImageOpt{}
}
op.ImageOpt.NoConfig = metareq.Image.NoConfig
op.ImageOpt.AttestationChain = metareq.Image.AttestationChain
}
if metareq.Git != nil {
op.GitOpt = &sourceresolver.ResolveGitOpt{
ReturnObject: metareq.Git.ReturnObject,
}
}
resp, err := p.resolveSourceMetadata(ctx, metareq.Source, op, false)
if err != nil {
return false, errors.Wrap(err, "error resolving source metadata from policy request")
}
req.Source = &gatewaypb.ResolveSourceMetaResponse{
Source: resp.Op,
}
if resp.Image != nil {
req.Source.Image = &gatewaypb.ResolveSourceImageResponse{
Digest: resp.Image.Digest.String(),
Config: resp.Image.Config,
}
}
req.Source = gateway.ToPBResolveSourceMetaResponse(resp)
continue
}

2
vendor/modules.txt vendored
View File

@@ -838,7 +838,7 @@ github.com/mitchellh/hashstructure/v2
# github.com/mitchellh/reflectwalk v1.0.2
## explicit
github.com/mitchellh/reflectwalk
# github.com/moby/buildkit v0.26.2
# github.com/moby/buildkit v0.26.3
## explicit; go 1.24.3
github.com/moby/buildkit/api/services/control
github.com/moby/buildkit/api/types