From ecfc488eea2624568494a0b0d33e0ecb501f3290 Mon Sep 17 00:00:00 2001 From: Areeb Ahmed Date: Wed, 20 May 2026 00:38:35 +0300 Subject: [PATCH 1/5] track loadbalance mode Signed-off-by: Areeb Ahmed --- build/resolver/driver.go | 8 ++++++++ driver/kubernetes/factory.go | 1 + 2 files changed, 9 insertions(+) diff --git a/build/resolver/driver.go b/build/resolver/driver.go index f68cd3350cb2..27c64fa66c7f 100644 --- a/build/resolver/driver.go +++ b/build/resolver/driver.go @@ -57,6 +57,14 @@ func (dp ResolvedNode) Platforms() []ocispecs.Platform { } func (dp ResolvedNode) Client(ctx context.Context) (*client.Client, error) { + node := dp.resolver.nodes[dp.driverIndex] + // For per-build drivers (e.g. loadbalance=random), bootstrap then return a fresh connection. + if node.Driver != nil && node.Driver.NeedsNewClientPerBuild() { + if _, err := dp.resolver.boot(ctx, []int{dp.driverIndex}, nil); err != nil { + return nil, err + } + return node.Driver.NewClient(ctx) + } clients, err := dp.resolver.boot(ctx, []int{dp.driverIndex}, nil) if err != nil { return nil, err diff --git a/driver/kubernetes/factory.go b/driver/kubernetes/factory.go index 284e6ab3946e..c13a721a19e4 100644 --- a/driver/kubernetes/factory.go +++ b/driver/kubernetes/factory.go @@ -162,6 +162,7 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver StatefulSet: d.statefulSet, } } + d.loadbalance = loadbalance return d, nil } From c2509d98910b4d730c7608f29a0b6b0639110714 Mon Sep 17 00:00:00 2001 From: Areeb Ahmed Date: Wed, 20 May 2026 00:39:15 +0300 Subject: [PATCH 2/5] expose client hooks Signed-off-by: Areeb Ahmed --- driver/manager.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/driver/manager.go b/driver/manager.go index 089e6a17377b..e1973451a351 100644 --- a/driver/manager.go +++ b/driver/manager.go @@ -128,6 +128,20 @@ func (d *DriverHandle) Client(ctx context.Context, opt ...client.ClientOpt) (*cl return d.client, d.err } +func (d *DriverHandle) NewClient(ctx context.Context) (*client.Client, error) { + return d.Driver.Client(ctx, d.getClientOptions()...) +} + +func (d *DriverHandle) NeedsNewClientPerBuild() bool { + type perBuildClientDriver interface { + NeedsNewClientPerBuild() bool + } + if p, ok := d.Driver.(perBuildClientDriver); ok { + return p.NeedsNewClientPerBuild() + } + return false +} + func (d *DriverHandle) getClientOptions() []client.ClientOpt { return []client.ClientOpt{ client.WithTracerDelegate(delegated.DefaultExporter), From 2ba49569147cee4e709a7569c2b6cbc9ac028f72 Mon Sep 17 00:00:00 2001 From: Areeb Ahmed Date: Wed, 20 May 2026 00:40:17 +0300 Subject: [PATCH 3/5] connect fresh client Signed-off-by: Areeb Ahmed --- driver/kubernetes/driver.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/driver/kubernetes/driver.go b/driver/kubernetes/driver.go index fdae382b6f1e..3da568cb625b 100644 --- a/driver/kubernetes/driver.go +++ b/driver/kubernetes/driver.go @@ -45,6 +45,7 @@ type Driver struct { // if you add fields, remember to update docs: // https://github.com/docker/docs/blob/main/content/build/drivers/kubernetes.md minReplicas int + loadbalance string deployment *appsv1.Deployment statefulSet *appsv1.StatefulSet configMaps []*corev1.ConfigMap @@ -61,6 +62,10 @@ func (d *Driver) IsMobyDriver() bool { return false } +func (d *Driver) NeedsNewClientPerBuild() bool { + return d.loadbalance == LoadbalanceRandom +} + func (d *Driver) Config() driver.InitConfig { return d.InitConfig } From a6cf181db470c4327e0c9045ebb08c58954daedf Mon Sep 17 00:00:00 2001 From: Areeb Ahmed Date: Wed, 20 May 2026 00:40:50 +0300 Subject: [PATCH 4/5] skip shared session Signed-off-by: Areeb Ahmed --- build/build.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/build.go b/build/build.go index 51b035f6dcc2..0a141646e461 100644 --- a/build/build.go +++ b/build/build.go @@ -1017,6 +1017,10 @@ func detectSharedMounts(ctx context.Context, reqs map[string][]*reqForNode) (_ m for _, reqs := range reqs { for _, req := range reqs { nodeName := req.ResolvedNode.Node().Name + // skip shared-session optimisation: targets may connect to different replicas. + if req.ResolvedNode.Node().Driver != nil && req.ResolvedNode.Node().Driver.NeedsNewClientPerBuild() { + continue + } if _, ok := m[nodeName]; !ok { m[nodeName] = map[fsKey]*fsTracker{} } From 793a2f6613f37691a29a7522ca1e6f5023743c96 Mon Sep 17 00:00:00 2001 From: Areeb Ahmed Date: Wed, 20 May 2026 00:41:13 +0300 Subject: [PATCH 5/5] add test Signed-off-by: Areeb Ahmed --- driver/kubernetes/factory_test.go | 38 +++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/driver/kubernetes/factory_test.go b/driver/kubernetes/factory_test.go index 9219b3893422..1d16e6132ba5 100644 --- a/driver/kubernetes/factory_test.go +++ b/driver/kubernetes/factory_test.go @@ -268,3 +268,41 @@ func TestFactory_processDriverOpts(t *testing.T) { }, ) } + +func TestNeedsNewClientPerBuild(t *testing.T) { + f := factory{ + cc: &mockClientConfig{ + clientConfig: &rest.Config{}, + }, + } + baseCfg := driver.InitConfig{ + Name: driver.BuilderName("test"), + } + + t.Run("RandomLoadbalance", func(t *testing.T) { + cfg := baseCfg + cfg.DriverOpts = map[string]string{"loadbalance": "random"} + d, err := f.New(t.Context(), cfg) + require.NoError(t, err) + require.True(t, d.(*Driver).NeedsNewClientPerBuild(), + "expected NeedsNewClientPerBuild=true for loadbalance=random") + }) + + t.Run("StickyLoadbalance", func(t *testing.T) { + cfg := baseCfg + cfg.DriverOpts = map[string]string{"loadbalance": "sticky"} + d, err := f.New(t.Context(), cfg) + require.NoError(t, err) + require.False(t, d.(*Driver).NeedsNewClientPerBuild(), + "expected NeedsNewClientPerBuild=false for loadbalance=sticky") + }) + + t.Run("DefaultLoadbalance", func(t *testing.T) { + cfg := baseCfg + cfg.DriverOpts = map[string]string{} + d, err := f.New(t.Context(), cfg) + require.NoError(t, err) + require.False(t, d.(*Driver).NeedsNewClientPerBuild(), + "expected NeedsNewClientPerBuild=false for default (sticky) loadbalance") + }) +}