Skip to content

Commit 4ec0789

Browse files
committed
feat: bump go-conainerregistry
Add options to configure debugging and setting default parallel jobs. See: siderolabs/talos#13462 Signed-off-by: Noel Georgi <git@frezbo.dev>
1 parent 425e59e commit 4ec0789

11 files changed

Lines changed: 281 additions & 20 deletions

File tree

cmd/image-factory/cmd/options.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"net/url"
1010
"strings"
1111
"time"
12+
13+
"github.com/siderolabs/image-factory/internal/remotewrap"
1214
)
1315

1416
// Validate checks Options for inconsistencies that would otherwise produce
@@ -46,6 +48,12 @@ func (o *Options) Validate() error {
4648
}
4749
}
4850

51+
// 0 means "unset" (falls back to remotewrap.DefaultJobs); any explicit value must not be
52+
// below the default, as lower concurrency can deadlock the limiter under load.
53+
if o.Registry.Jobs != 0 && o.Registry.Jobs < remotewrap.DefaultJobs {
54+
return fmt.Errorf("registry.jobs must be >= %d if set, got %d", remotewrap.DefaultJobs, o.Registry.Jobs)
55+
}
56+
4957
return nil
5058
}
5159

@@ -79,6 +87,26 @@ type Options struct { //nolint:govet // keeping order for semantic clarity
7987

8088
// Enterprise contains configuration for enterprise-specific features.
8189
Enterprise EnterpriseOptions `koanf:"enterprise"`
90+
91+
// Registry contains low-level tuning for the registry client (pull/push concurrency, debugging).
92+
Registry RegistryOptions `koanf:"registry"`
93+
}
94+
95+
// RegistryOptions tunes the shared registry client used for all pull/push operations.
96+
type RegistryOptions struct {
97+
// Jobs is the maximum number of concurrent blob pull/push operations per registry client.
98+
//
99+
// go-containerregistry gates concurrent blob fetches on this value; too low a value can
100+
// deadlock under Image Factory's concurrent, multiplexed fetch pattern.
101+
// Defaults to remotewrap.DefaultJobs.
102+
Jobs int `koanf:"jobs"`
103+
104+
// Debug tracks registry response bodies to help diagnose pull-limiter token leaks/stalls:
105+
// it periodically logs how many bodies are open and dumps any body that stays open too long
106+
// together with the stack that opened it.
107+
//
108+
// Set via config or the IF_REGISTRY_DEBUG environment variable.
109+
Debug bool `koanf:"debug"`
82110
}
83111

84112
// AssetBuilderOptions contains settings for building assets.
@@ -606,4 +634,8 @@ var DefaultOptions = Options{
606634
},
607635
},
608636
},
637+
638+
Registry: RegistryOptions{
639+
Jobs: remotewrap.DefaultJobs,
640+
},
609641
}

cmd/image-factory/cmd/service.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ func RunFactory(ctx context.Context, logger *zap.Logger, opts Options) error {
6666
)
6767
defer logger.Info("shutting down", zap.String("name", version.Name))
6868

69+
// apply registry client tuning before any puller/pusher is constructed
70+
remotewrap.SetJobs(opts.Registry.Jobs)
71+
remotewrap.SetDebug(opts.Registry.Debug)
72+
6973
// many image generation steps rely on SOURCE_DATE_EPOCH
7074
// to ensure reproducibility, set it to a fixed value
7175
if err := os.Setenv("SOURCE_DATE_EPOCH", "1559424892"); err != nil { // this value matches `pkgs` SOURCE_DATE_EPOCH
@@ -605,7 +609,7 @@ func buildSchematicFactory(ctx context.Context, logger *zap.Logger, eg *errgroup
605609
//
606610
// Enable registry auth from the standard Docker config, and from GitHub via the token.
607611
func remoteOptions() []remote.Option {
608-
return []remote.Option{
612+
opts := []remote.Option{
609613
remote.WithAuthFromKeychain(
610614
authn.NewMultiKeychain(
611615
authn.DefaultKeychain,
@@ -614,6 +618,15 @@ func remoteOptions() []remote.Option {
614618
),
615619
),
616620
}
621+
622+
// When registry debugging is enabled, route cosign's remote operations through
623+
// the same instrumented transport so leaked blob/manifest bodies (i.e. leaked
624+
// pull-limiter tokens) in the sign/verify paths are tracked too.
625+
if remotewrap.DebugEnabled() {
626+
opts = append(opts, remote.WithTransport(remotewrap.RoundTripper()))
627+
}
628+
629+
return opts
617630
}
618631

619632
// buildCacheSigner constructs the image signer from options.

deploy/helm/image-factory/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ extraVolumeMounts:
326326
| cacheSigningKey | object | `{"existingSecret":"","key":""}` | Image Cache Signing Key Configuration # This secret contains the ECDSA private key used to sign cached Talos image artifacts. # This ensures that nodes can verify the integrity of images served by the Image Factory. # If you are running a self-hosted Image Factory, this key is required. |
327327
| cacheSigningKey.existingSecret | string | `""` | Name of an existing Secret containing the ECDSA private key. IMPORTANT: The existing secret MUST contain a data key named exactly "cache-signing.key". If your secret uses a different key, Image Factory will not find the file. Example creation: kubectl create secret generic image-factory-cache-signing-key --from-file=cache-signing.key=./signing-key.key |
328328
| cacheSigningKey.key | string | `""` | If 'existingSecret' is empty, a new Secret will be created. The ECDSA private key content (multiline string). Generate using: openssl ecparam -name prime256v1 -genkey -noout -out cache-signing.key |
329-
| config | object | `{"artifacts":{"schematic":{"insecure":false,"namespace":"siderolabs/image-factory","registry":"registry.example.com","repository":"schematics"}},"authentication":{"enabled":false,"htpasswdPath":"/etc/image-factory/auth/htpasswd"},"cache":{"signingKeyPath":"/etc/image-factory/keys/cache-signing.key"}}` | Sidero Image-Factory Configuration |
329+
| config | object | `{"artifacts":{"schematic":{"insecure":false,"namespace":"siderolabs/image-factory","registry":"registry.example.com","repository":"schematics"}},"authentication":{"enabled":false,"htpasswdPath":"/etc/image-factory/auth/htpasswd"},"cache":{"signingKeyPath":"/etc/image-factory/keys/cache-signing.key"},"registry":{"debug":false,"jobs":64}}` | Sidero Image-Factory Configuration |
330330
| env | list | `[]` | Environment variables to pass to Image Factory |
331331
| envFrom | list | `[]` | envFrom to pass to Image Factory |
332332
| extraObjects | list | `[]` | |

deploy/helm/image-factory/values.schema.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,18 @@
8686
"type": "string"
8787
}
8888
}
89+
},
90+
"registry": {
91+
"type": "object",
92+
"properties": {
93+
"debug": {
94+
"type": "boolean"
95+
},
96+
"jobs": {
97+
"type": "integer",
98+
"minimum": 64
99+
}
100+
}
89101
}
90102
},
91103
"additionalProperties": true

deploy/helm/image-factory/values.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,19 @@ config:
237237
## Repository name for VEX data
238238
# repository: talos-vex-data
239239

240+
## Registry Client Tuning
241+
## Low-level tuning for the shared registry client used for all pull/push operations.
242+
registry:
243+
## Maximum number of concurrent blob pull/push operations per registry client.
244+
## Must be >= 64; lower values can deadlock the go-containerregistry pull limiter
245+
## under Image Factory's concurrent, multiplexed fetch pattern.
246+
# @schema minimum:64
247+
jobs: 64
248+
## Track registry response bodies to diagnose pull-limiter token leaks/stalls: periodically
249+
## logs how many bodies are open and dumps any body that stays open too long together with the
250+
## stack that opened it. Can also be set via the IF_REGISTRY_DEBUG environment variable.
251+
debug: false
252+
240253
# -- Environment variables to pass to Image Factory
241254
env: []
242255

docs/configuration.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,38 @@ Capacity caps the number of cached objects before LRU eviction.
10171017

10181018
---
10191019

1020+
### `registry`
1021+
1022+
Registry contains low-level tuning for the registry client (pull/push concurrency, debugging).
1023+
1024+
---
1025+
1026+
### `registry.jobs`
1027+
1028+
- **Type:** `int`
1029+
- **Env:** `REGISTRY_JOBS`
1030+
1031+
Jobs is the maximum number of concurrent blob pull/push operations per registry client.
1032+
1033+
go-containerregistry gates concurrent blob fetches on this value; too low a value can
1034+
deadlock under Image Factory's concurrent, multiplexed fetch pattern.
1035+
Defaults to remotewrap.DefaultJobs.
1036+
1037+
---
1038+
1039+
### `registry.debug`
1040+
1041+
- **Type:** `bool`
1042+
- **Env:** `REGISTRY_DEBUG`
1043+
1044+
Debug tracks registry response bodies to help diagnose pull-limiter token leaks/stalls:
1045+
it periodically logs how many bodies are open and dumps any body that stays open too long
1046+
together with the stack that opened it.
1047+
1048+
Set via config or the IF_REGISTRY_DEBUG environment variable.
1049+
1050+
---
1051+
10201052
## Default Configuration
10211053

10221054
### YAML
@@ -1122,6 +1154,9 @@ http:
11221154
keyFile: ""
11231155
metrics:
11241156
addr: :2122
1157+
registry:
1158+
debug: false
1159+
jobs: 64
11251160
secureBoot:
11261161
awsKMS:
11271162
certARN: ""
@@ -1216,6 +1251,8 @@ IF_HTTP_EXTERNALURL=https://localhost/
12161251
IF_HTTP_HTTPLISTENADDR=:8080
12171252
IF_HTTP_KEYFILE=
12181253
IF_METRICS_ADDR=:2122
1254+
IF_REGISTRY_DEBUG=false
1255+
IF_REGISTRY_JOBS=64
12191256
IF_SECUREBOOT_AWSKMS_CERTARN=
12201257
IF_SECUREBOOT_AWSKMS_CERTPATH=
12211258
IF_SECUREBOOT_AWSKMS_KEYID=

go.mod

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,6 @@ module github.com/siderolabs/image-factory
22

33
go 1.26.3
44

5-
// downgrade the go-containerregistry to workaround the security changes in
6-
// https://github.com/google/go-containerregistry/pull/2227/changes: it breaks
7-
// our extensions
8-
replace github.com/google/go-containerregistry => github.com/google/go-containerregistry v0.21.2
9-
105
require (
116
cloud.google.com/go/auth v0.20.0
127
github.com/CalebQ42/squashfs v1.4.1
@@ -179,7 +174,6 @@ require (
179174
github.com/containerd/log v0.1.0 // indirect
180175
github.com/containerd/platforms v1.0.0-rc.4 // indirect
181176
github.com/containerd/plugin v1.1.0 // indirect
182-
github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect
183177
github.com/containerd/ttrpc v1.2.8 // indirect
184178
github.com/containerd/typeurl/v2 v2.2.3 // indirect
185179
github.com/containernetworking/cni v1.3.0 // indirect
@@ -194,7 +188,6 @@ require (
194188
github.com/diskfs/go-diskfs v1.7.0 // indirect
195189
github.com/distribution/reference v0.6.0 // indirect
196190
github.com/docker/cli v29.4.3+incompatible // indirect
197-
github.com/docker/distribution v2.8.3+incompatible // indirect
198191
github.com/docker/docker-credential-helpers v0.9.5 // indirect
199192
github.com/docker/go-connections v0.7.0 // indirect
200193
github.com/docker/go-units v0.5.0 // indirect
@@ -433,7 +426,6 @@ require (
433426
github.com/transparency-dev/merkle v0.0.2 // indirect
434427
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
435428
github.com/vbatts/go-mtree v0.7.0 // indirect
436-
github.com/vbatts/tar-split v0.12.2 // indirect
437429
github.com/vifraa/gopom v1.0.0 // indirect
438430
github.com/vultr/metadata v1.1.0 // indirect
439431
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 // indirect

go.sum

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,6 @@ github.com/containerd/platforms v1.0.0-rc.4 h1:M42JrUT4zfZTqtkUwkr0GzmUWbfyO5VO0
378378
github.com/containerd/platforms v1.0.0-rc.4/go.mod h1:lKlMXyLybmBedS/JJm11uDofzI8L2v0J2ZbYvNsbq1A=
379379
github.com/containerd/plugin v1.1.0 h1:O+7lczNJVMy8rz0YNx3xGB8tTf5qY4i5abF041Ew19U=
380380
github.com/containerd/plugin v1.1.0/go.mod h1:qBTum+A8lJ6lO44A19Eo7y1OlcLj4OWFH1DA/vnHmcc=
381-
github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw=
382-
github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY=
383381
github.com/containerd/ttrpc v1.2.8 h1:xbVu6D4qF2jihdh9rDVOKqUMiFBQk6YctTdo1zk087Y=
384382
github.com/containerd/ttrpc v1.2.8/go.mod h1:wyZW2K79t4Hfcxl+GUvkZqRBzJlqFFvgEeeWXa42tyE=
385383
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
@@ -424,8 +422,6 @@ github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz
424422
github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
425423
github.com/docker/cli v29.4.3+incompatible h1:u+UliYm2J/rYrIh2FqHQg32neRG8GjbvNuwQRTzGspU=
426424
github.com/docker/cli v29.4.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
427-
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
428-
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
429425
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
430426
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
431427
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
@@ -694,8 +690,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
694690
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
695691
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
696692
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
697-
github.com/google/go-containerregistry v0.21.2 h1:vYaMU4nU55JJGFC9JR/s8NZcTjbE9DBBbvusTW9NeS0=
698-
github.com/google/go-containerregistry v0.21.2/go.mod h1:ctO5aCaewH4AK1AumSF5DPW+0+R+d2FmylMJdp5G7p0=
693+
github.com/google/go-containerregistry v0.21.6 h1:T+yqQIlJXKrM98Om4DlW3GoWQAmhZuLMwoDOvVrtiUM=
694+
github.com/google/go-containerregistry v0.21.6/go.mod h1:U7MMSBIJynke2MVQrQk19NP9k/uQsGz/h0amIFSHMbo=
699695
github.com/google/go-github/v73 v73.0.0 h1:aR+Utnh+Y4mMkS+2qLQwcQ/cF9mOTpdwnzlaw//rG24=
700696
github.com/google/go-github/v73 v73.0.0/go.mod h1:fa6w8+/V+edSU0muqdhCVY7Beh1M8F1IlQPZIANKIYw=
701697
github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0=
@@ -1423,8 +1419,6 @@ github.com/umisama/go-cpe v0.0.0-20190323060751-cdd6c3c28a23 h1:+168JmE638t0Oxro
14231419
github.com/umisama/go-cpe v0.0.0-20190323060751-cdd6c3c28a23/go.mod h1:Jv/KoYWD3+46wW8r3pEwISwtgv5Q8NTfFto2wFRKvoA=
14241420
github.com/vbatts/go-mtree v0.7.0 h1:ytmOc3MTRidZiBi9VBCyZ2BHe4fZS47L5v7BVXDWW4E=
14251421
github.com/vbatts/go-mtree v0.7.0/go.mod h1:EjdpFC+LZy1TXbRGNa1MKKgjQ+7ew3foMFJK8o4/TdY=
1426-
github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=
1427-
github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
14281422
github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0=
14291423
github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
14301424
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=

internal/asset/cache/s3/reference.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ func newObjectReference(asset cache.BootAsset) (*objectReference, error) {
2222
return nil, err
2323
}
2424

25+
defer r.Close() //nolint:errcheck
26+
2527
obj := &objectReference{}
2628

2729
err = json.NewDecoder(r).Decode(obj)

0 commit comments

Comments
 (0)