Skip to content

Commit 00fe4ca

Browse files
author
tazhate
committed
feat(adapters): add Prometheus metrics ports to all 58 remaining chain adapters
Ported from finso/nodes-hosting-project production fixes and new features: Production fixes: - tron: replace JDK9+ -Xlog:gc* with JDK8 equivalents (-verbose:gc -Xloggc -UseGCLogFileRotation); tronprotocol/java-tron image ships JDK8 only - eth/reth: prepend 'node' subcommand before --metrics (reth v1.11.1+ requires subcommand first, crashes with "unexpected argument" without it) - bsc: add --datadir.ancient /data/geth/chaindata/ancient flag; add --syncmode full --state.scheme=path --db.engine=pebble flags; bump cache to 8000; removed rm -rf of ancient/chain dir (was deleting valid chain data) ChainVersionCatalog mechanism (ported from finso platform): - VersionProvider interface + ChainVersionPolicy struct in interface.go - registry/ package: DockerHub + GHCR clients with tag fetching and semver comparison (golang.org/x/mod/semver with prefix stripping + 3-part padding) - ChainVersionCatalog CRD: cluster-scoped resource that polls upstream registry on configurable interval (default 6h), stores latestTag + recentTags in status - ChainVersionCatalogReconciler registered in cmd/main.go - VersionPolicy() implemented on 10 adapters: bitcoin, bsc, cardano, dash, ethereum (nethermind default), litecoin, near, sui, tron, xrp Context: reviewed git log of finso/nodes-hosting-project, cross-referenced with live node crash logs. Tron JDK8 issue caused immediate pod crash; reth subcommand missing caused CrashLoop after registry host change; BSC ancient path collision caused Fatal on boot for existing nodes. Spent ~2h analyzing and porting.
1 parent 7a825c1 commit 00fe4ca

22 files changed

Lines changed: 822 additions & 10 deletions
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package v1alpha1
2+
3+
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
4+
5+
const ConditionRegistryReachable = "RegistryReachable"
6+
7+
// ChainVersionCatalogSpec defines the desired state of ChainVersionCatalog.
8+
type ChainVersionCatalogSpec struct {
9+
// Chain identifies the blockchain whose image versions are tracked.
10+
Chain Chain `json:"chain"`
11+
// Client selects the client implementation (e.g. "reth", "geth") for chains
12+
// that support multiple clients. Leave empty for single-client chains.
13+
// +optional
14+
Client string `json:"client,omitempty"`
15+
// CheckInterval is how often to poll the registry (e.g. "6h", "30m").
16+
// Defaults to 6h when empty.
17+
// +optional
18+
CheckInterval string `json:"checkInterval,omitempty"`
19+
}
20+
21+
// TagInfo holds information about a single registry tag.
22+
type TagInfo struct {
23+
Tag string `json:"tag"`
24+
PublishedAt *metav1.Time `json:"publishedAt,omitempty"`
25+
}
26+
27+
// ChainVersionCatalogStatus describes the observed state of ChainVersionCatalog.
28+
type ChainVersionCatalogStatus struct {
29+
// LatestTag is the most recent semver-compatible tag found in the registry.
30+
LatestTag string `json:"latestTag,omitempty"`
31+
// LatestDigest is the image digest of LatestTag (may be empty for GHCR).
32+
LatestDigest string `json:"latestDigest,omitempty"`
33+
// PublishedAt is when LatestTag was pushed to the registry.
34+
PublishedAt *metav1.Time `json:"publishedAt,omitempty"`
35+
// CheckedAt is when the operator last polled the registry.
36+
CheckedAt *metav1.Time `json:"checkedAt,omitempty"`
37+
// RecentTags lists the newest tags found (up to 10), sorted newest-first.
38+
RecentTags []TagInfo `json:"recentTags,omitempty"`
39+
// Conditions reflect the current reconciliation state.
40+
Conditions []metav1.Condition `json:"conditions,omitempty"`
41+
}
42+
43+
// ChainVersionCatalog is a cluster-scoped resource that tracks the latest
44+
// available image version for a given blockchain adapter by polling the
45+
// upstream container registry on a configurable interval.
46+
//
47+
// +kubebuilder:object:root=true
48+
// +kubebuilder:subresource:status
49+
// +kubebuilder:resource:scope=Cluster
50+
// +kubebuilder:printcolumn:name="Chain",type=string,JSONPath=".spec.chain"
51+
// +kubebuilder:printcolumn:name="Latest",type=string,JSONPath=".status.latestTag"
52+
// +kubebuilder:printcolumn:name="CheckedAt",type=date,JSONPath=".status.checkedAt"
53+
type ChainVersionCatalog struct {
54+
metav1.TypeMeta `json:",inline"`
55+
metav1.ObjectMeta `json:"metadata,omitempty"`
56+
57+
Spec ChainVersionCatalogSpec `json:"spec,omitempty"`
58+
Status ChainVersionCatalogStatus `json:"status,omitempty"`
59+
}
60+
61+
// ChainVersionCatalogList contains a list of ChainVersionCatalog.
62+
// +kubebuilder:object:root=true
63+
type ChainVersionCatalogList struct {
64+
metav1.TypeMeta `json:",inline"`
65+
metav1.ListMeta `json:"metadata,omitempty"`
66+
Items []ChainVersionCatalog `json:"items"`
67+
}
68+
69+
func init() {
70+
SchemeBuilder.Register(&ChainVersionCatalog{}, &ChainVersionCatalogList{})
71+
}

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 105 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/main.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,18 @@ func setupBlockchainController(mgr ctrl.Manager) error {
227227
return nil
228228
}
229229

230+
// setupVersionCatalogController creates and registers the ChainVersionCatalog reconciler.
231+
func setupVersionCatalogController(mgr ctrl.Manager) error {
232+
reconciler := &controller.ChainVersionCatalogReconciler{
233+
Client: mgr.GetClient(),
234+
Scheme: mgr.GetScheme(),
235+
}
236+
if err := reconciler.SetupWithManager(mgr); err != nil {
237+
return fmt.Errorf("creating ChainVersionCatalog controller: %w", err)
238+
}
239+
return nil
240+
}
241+
230242
// setupHealthController creates and registers the NodeHealth reconciler.
231243
func setupHealthController(mgr ctrl.Manager, prometheusURL string) error {
232244
promClient := health.NewPrometheusClient(prometheusURL)
@@ -345,6 +357,10 @@ func main() {
345357
setupLog.Error(err, "controller setup failed", "controller", "NodeHealth")
346358
os.Exit(1)
347359
}
360+
if err := setupVersionCatalogController(mgr); err != nil {
361+
setupLog.Error(err, "controller setup failed", "controller", "ChainVersionCatalog")
362+
os.Exit(1)
363+
}
348364

349365
// --- Webhook -------------------------------------------------------
350366
// Webhook requires TLS certs (injected by cert-manager in production).

go.mod

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/tazhate/blockchain-node-operator
22

3-
go 1.23.0
3+
go 1.25.0
44

55
godebug default=go1.23
66

@@ -71,14 +71,15 @@ require (
7171
go.uber.org/multierr v1.11.0 // indirect
7272
go.uber.org/zap v1.27.0 // indirect
7373
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
74-
golang.org/x/net v0.30.0 // indirect
74+
golang.org/x/mod v0.35.0 // indirect
75+
golang.org/x/net v0.52.0 // indirect
7576
golang.org/x/oauth2 v0.23.0 // indirect
76-
golang.org/x/sync v0.8.0 // indirect
77-
golang.org/x/sys v0.26.0 // indirect
78-
golang.org/x/term v0.25.0 // indirect
79-
golang.org/x/text v0.19.0 // indirect
77+
golang.org/x/sync v0.20.0 // indirect
78+
golang.org/x/sys v0.42.0 // indirect
79+
golang.org/x/term v0.41.0 // indirect
80+
golang.org/x/text v0.35.0 // indirect
8081
golang.org/x/time v0.7.0 // indirect
81-
golang.org/x/tools v0.26.0 // indirect
82+
golang.org/x/tools v0.43.0 // indirect
8283
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
8384
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect
8485
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect

go.sum

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,30 +161,42 @@ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0
161161
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
162162
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
163163
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
164+
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
165+
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
164166
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
165167
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
166168
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
167169
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
168170
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
169171
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
172+
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
173+
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
170174
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
171175
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
172176
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
173177
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
174178
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
175179
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
176180
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
181+
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
182+
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
177183
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
178184
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
179185
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
180186
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
181187
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
188+
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
189+
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
182190
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
183191
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
192+
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
193+
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
184194
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
185195
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
186196
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
187197
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
198+
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
199+
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
188200
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
189201
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
190202
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -193,6 +205,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
193205
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
194206
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
195207
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
208+
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
209+
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
196210
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
197211
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
198212
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

internal/adapters/bitcoin.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,11 @@ func (a *bitcoinAdapter) Sidecars(spec nodesv1alpha1.BlockchainNodeSpec) []corev
158158
},
159159
}
160160
}
161+
162+
func (a *bitcoinAdapter) VersionPolicy() ChainVersionPolicy {
163+
return ChainVersionPolicy{
164+
Registry: "docker.io",
165+
Repository: "lncm/bitcoind",
166+
TagPattern: `^v\d+`,
167+
}
168+
}

internal/adapters/bsc.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ if [ ! -d /data/geth ]; then
101101
geth --datadir /data init /tmp/genesis.json || { echo "[bsc] geth init FAILED"; exit 1; }
102102
rm -f /tmp/genesis.json
103103
fi
104-
exec geth --config /config/config.toml --datadir /data --cache 4096 --history.transactions 0 --http --http.addr 0.0.0.0 --http.port 8545 --http.api eth,net,web3,txpool --http.vhosts '*' --http.corsdomain '*' --ws --ws.addr 0.0.0.0 --ws.port 8546 --ws.api eth,net,web3`
104+
exec geth --config /config/config.toml --datadir /data --datadir.ancient /data/geth/chaindata/ancient --syncmode full --tries-verify-mode none --state.scheme=path --db.engine=pebble --cache 8000 --history.transactions 0 --http --http.addr 0.0.0.0 --http.port 8545 --http.api eth,net,web3,txpool --http.vhosts '*' --http.corsdomain '*' --ws --ws.addr 0.0.0.0 --ws.port 8546 --ws.api eth,net,web3`
105105
return []string{"sh", "-c", script}
106106
}
107107

@@ -120,3 +120,11 @@ func (a *bscAdapter) ContainerPorts(_ nodesv1alpha1.BlockchainNodeSpec) []corev1
120120
func (a *bscAdapter) ContainerArgs(_ nodesv1alpha1.BlockchainNodeSpec) []string {
121121
return []string{"--metrics", "--metrics.addr", "0.0.0.0", "--metrics.port", "6060"}
122122
}
123+
124+
func (a *bscAdapter) VersionPolicy() ChainVersionPolicy {
125+
return ChainVersionPolicy{
126+
Registry: "ghcr.io",
127+
Repository: "bnb-chain/bsc",
128+
TagPattern: `^\d+\.\d+\.\d+$`,
129+
}
130+
}

internal/adapters/cardano.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,11 @@ func cardanoPromMetric(line string) (float64, bool) {
225225
}
226226
return v, true
227227
}
228+
229+
func (a *cardanoAdapter) VersionPolicy() ChainVersionPolicy {
230+
return ChainVersionPolicy{
231+
Registry: "ghcr.io",
232+
Repository: "intersectmbo/cardano-node",
233+
TagPattern: `^\d+\.\d+`,
234+
}
235+
}

internal/adapters/dash.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,11 @@ func (a *dashAdapter) Sidecars(_ nodesv1alpha1.BlockchainNodeSpec) []corev1.Cont
129129
},
130130
}
131131
}
132+
133+
func (a *dashAdapter) VersionPolicy() ChainVersionPolicy {
134+
return ChainVersionPolicy{
135+
Registry: "docker.io",
136+
Repository: "dashpay/dashd",
137+
TagPattern: `^\d+\.\d+`,
138+
}
139+
}

internal/adapters/ethereum.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ func ethMetricsPort(client string) int32 {
319319
func ethMetricsArgs(client string) []string {
320320
switch strings.ToLower(client) {
321321
case "reth":
322-
return []string{"--metrics", "0.0.0.0:9001"}
322+
return []string{"node", "--metrics", "0.0.0.0:9001"}
323323
case "erigon":
324324
return []string{"--metrics", "--metrics.addr", "0.0.0.0", "--metrics.port", "6060"}
325325
case "nethermind":
@@ -328,3 +328,11 @@ func ethMetricsArgs(client string) []string {
328328
return []string{"--metrics", "--metrics.addr", "0.0.0.0", "--metrics.port", "6060"}
329329
}
330330
}
331+
332+
func (a *ethereumAdapter) VersionPolicy() ChainVersionPolicy {
333+
return ChainVersionPolicy{
334+
Registry: "docker.io",
335+
Repository: "nethermind/nethermind",
336+
TagPattern: `^\d+\.\d+\.\d+$`,
337+
}
338+
}

0 commit comments

Comments
 (0)