mirror of
https://github.com/luxfi/charts.git
synced 2026-09-24 00:24:06 +00:00
node: a deployable validator chart, pinned by digest
The chart carried no templates, so nothing could deploy from it and every network ran its own hand-written manifests at a different node build. One chart now serves all three networks, with per-network values selecting only the network id: Lux (1), Hanzo (36963), Zoo (200200). The image resolves from an explicit digest, falling back to the chart's appVersion — a floating tag can no longer reach a cluster. Probes address the node's own endpoints: readiness reports bootstrap and router state, liveness reports only that the process serves, and a startup window grants 30 minutes to bootstrap before liveness counts. Named "node", not "lux-node" — the repository already says whose it is.
This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
apiVersion: v2
|
||||
name: lux-node
|
||||
description: Lux Network blockchain node
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "1.0.0"
|
||||
home: https://lux.network
|
||||
icon: https://lux.network/icon.png
|
||||
maintainers:
|
||||
- name: Lux Network
|
||||
email: [email protected]
|
||||
@@ -1,5 +0,0 @@
|
||||
replicaCount: 1
|
||||
image:
|
||||
repository: ghcr.io/luxfi/node
|
||||
tag: latest
|
||||
pullPolicy: Always
|
||||
@@ -0,0 +1,2 @@
|
||||
.git/
|
||||
*.tgz
|
||||
@@ -0,0 +1,17 @@
|
||||
apiVersion: v2
|
||||
name: node
|
||||
description: A Lux network validator — luxd, its chains and its peer identity.
|
||||
type: application
|
||||
version: 0.1.0
|
||||
# The node build this chart deploys unless values pin one explicitly.
|
||||
appVersion: "1.36.181"
|
||||
home: https://lux.network
|
||||
sources:
|
||||
- https://github.com/luxfi/node
|
||||
maintainers:
|
||||
- name: Lux Industries Inc
|
||||
url: https://lux.network
|
||||
keywords:
|
||||
- lux
|
||||
- validator
|
||||
- consensus
|
||||
@@ -0,0 +1,63 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "node.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
*/}}
|
||||
{{- define "node.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "node.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "node.labels" -}}
|
||||
helm.sh/chart: {{ include "node.chart" . }}
|
||||
{{ include "node.selector" . }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
lux.network/id: {{ .Values.network.id | quote }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "node.selector" -}}
|
||||
app.kubernetes.io/name: {{ include "node.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "node.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "node.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
The image reference. A digest pins exactly and wins over the tag; otherwise the
|
||||
tag, defaulting to the chart's appVersion. Never "latest".
|
||||
*/}}
|
||||
{{- define "node.image" -}}
|
||||
{{- $tag := default .Chart.AppVersion .Values.image.tag -}}
|
||||
{{- if .Values.image.digest -}}
|
||||
{{ .Values.image.repository }}:{{ $tag }}@{{ .Values.image.digest }}
|
||||
{{- else -}}
|
||||
{{ .Values.image.repository }}:{{ $tag }}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,11 @@
|
||||
{{- if gt (int .Values.replicas) 1 }}
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: {{ include "node.fullname" . }}
|
||||
labels: {{- include "node.labels" . | nindent 4 }}
|
||||
spec:
|
||||
maxUnavailable: {{ .Values.budget.maxUnavailable }}
|
||||
selector:
|
||||
matchLabels: {{- include "node.selector" . | nindent 6 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,16 @@
|
||||
{{- if or .Values.network.genesis .Values.network.chains }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "node.fullname" . }}
|
||||
labels: {{- include "node.labels" . | nindent 4 }}
|
||||
data:
|
||||
{{- if .Values.network.genesis }}
|
||||
genesis.json: |
|
||||
{{ .Values.network.genesis | indent 4 }}
|
||||
{{- end }}
|
||||
{{- range $alias, $cfg := .Values.network.chains }}
|
||||
chain-{{ $alias }}.json: |
|
||||
{{ toPrettyJson $cfg | indent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,34 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "node.fullname" . }}
|
||||
labels: {{- include "node.labels" . | nindent 4 }}
|
||||
{{- with .Values.service.annotations }}
|
||||
annotations: {{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
selector: {{- include "node.selector" . | nindent 4 }}
|
||||
ports:
|
||||
- name: http
|
||||
port: {{ .Values.ports.http }}
|
||||
targetPort: http
|
||||
---
|
||||
# Headless: every validator gets stable DNS so peers find each other by name
|
||||
# rather than by an address that moves.
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "node.fullname" . }}-peer
|
||||
labels: {{- include "node.labels" . | nindent 4 }}
|
||||
spec:
|
||||
clusterIP: None
|
||||
publishNotReadyAddresses: true
|
||||
selector: {{- include "node.selector" . | nindent 4 }}
|
||||
ports:
|
||||
- name: staking
|
||||
port: {{ .Values.ports.staking }}
|
||||
targetPort: staking
|
||||
- name: http
|
||||
port: {{ .Values.ports.http }}
|
||||
targetPort: http
|
||||
@@ -0,0 +1,10 @@
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "node.serviceAccountName" . }}
|
||||
labels: {{- include "node.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations: {{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,106 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: {{ include "node.fullname" . }}
|
||||
labels: {{- include "node.labels" . | nindent 4 }}
|
||||
spec:
|
||||
serviceName: {{ include "node.fullname" . }}-peer
|
||||
replicas: {{ .Values.replicas }}
|
||||
podManagementPolicy: Parallel
|
||||
selector:
|
||||
matchLabels: {{- include "node.selector" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels: {{- include "node.labels" . | nindent 8 }}
|
||||
annotations:
|
||||
# Roll the set when genesis or chain config changes.
|
||||
lux.network/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum | trunc 16 }}
|
||||
{{- with .Values.podAnnotations }}{{- toYaml . | nindent 8 }}{{- end }}
|
||||
spec:
|
||||
serviceAccountName: {{ include "node.serviceAccountName" . }}
|
||||
{{- with .Values.pullSecrets }}
|
||||
imagePullSecrets: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
securityContext:
|
||||
fsGroup: 1000
|
||||
containers:
|
||||
- name: node
|
||||
image: {{ include "node.image" . }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
args:
|
||||
- --network-id={{ .Values.network.id }}
|
||||
- --http-host=0.0.0.0
|
||||
- --http-port={{ .Values.ports.http }}
|
||||
- --staking-port={{ .Values.ports.staking }}
|
||||
- --data-dir=/data
|
||||
- --db-type={{ .Values.db }}
|
||||
{{- if .Values.network.genesis }}
|
||||
- --genesis-file=/genesis/genesis.json
|
||||
{{- end }}
|
||||
{{- if .Values.network.chains }}
|
||||
- --chain-config-dir=/chainconf
|
||||
{{- end }}
|
||||
{{- with .Values.args }}{{- toYaml . | nindent 12 }}{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.ports.http }}
|
||||
- name: staking
|
||||
containerPort: {{ .Values.ports.staking }}
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: {{ .Values.probes.liveness }}
|
||||
port: http
|
||||
periodSeconds: 10
|
||||
failureThreshold: {{ div .Values.probes.startupSeconds 10 }}
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: {{ .Values.probes.readiness }}
|
||||
port: http
|
||||
periodSeconds: 10
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: {{ .Values.probes.liveness }}
|
||||
port: http
|
||||
periodSeconds: 30
|
||||
failureThreshold: 5
|
||||
resources: {{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
{{- if .Values.network.genesis }}
|
||||
- name: config
|
||||
mountPath: /genesis/genesis.json
|
||||
subPath: genesis.json
|
||||
{{- end }}
|
||||
{{- range $alias, $_ := .Values.network.chains }}
|
||||
- name: config
|
||||
mountPath: /chainconf/{{ $alias }}/config.json
|
||||
subPath: chain-{{ $alias }}.json
|
||||
{{- end }}
|
||||
volumes:
|
||||
{{- if or .Values.network.genesis .Values.network.chains }}
|
||||
- name: config
|
||||
configMap:
|
||||
name: {{ include "node.fullname" . }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity: {{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
{{- if .Values.storage.class }}
|
||||
storageClassName: {{ .Values.storage.class }}
|
||||
{{- end }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.storage.size }}
|
||||
@@ -0,0 +1,7 @@
|
||||
# Hanzo L1 — sovereign, so its primary network id equals its EVM id.
|
||||
network:
|
||||
id: 36963
|
||||
|
||||
image:
|
||||
tag: "1.36.181"
|
||||
digest: "sha256:3ff4841e76b407e774b59ea33cdb691bc0b5b6dfdebc0e5ea71bd1119d059cad"
|
||||
@@ -0,0 +1,7 @@
|
||||
# Lux Network — the primary network. Its C-chain carries EVM id 96369.
|
||||
network:
|
||||
id: 1
|
||||
|
||||
image:
|
||||
tag: "1.36.181"
|
||||
digest: "sha256:3ff4841e76b407e774b59ea33cdb691bc0b5b6dfdebc0e5ea71bd1119d059cad"
|
||||
@@ -0,0 +1,7 @@
|
||||
# Zoo L1 — sovereign, so its primary network id equals its EVM id.
|
||||
network:
|
||||
id: 200200
|
||||
|
||||
image:
|
||||
tag: "1.36.181"
|
||||
digest: "sha256:3ff4841e76b407e774b59ea33cdb691bc0b5b6dfdebc0e5ea71bd1119d059cad"
|
||||
@@ -0,0 +1,73 @@
|
||||
# A validator set for one network. Per-network values live beside this file
|
||||
# (values-lux.yaml, values-hanzo.yaml, values-zoo.yaml); the universe repos
|
||||
# supply the exact pin per environment.
|
||||
|
||||
replicas: 5
|
||||
|
||||
image:
|
||||
repository: ghcr.io/luxfi/node
|
||||
# Empty tag follows Chart.appVersion. Set digest to pin exactly — a digest
|
||||
# wins over the tag, and a floating tag never reaches a cluster.
|
||||
tag: ""
|
||||
digest: ""
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
pullSecrets: []
|
||||
|
||||
# The network these validators form. id is the primary network id: 1/2/3 for
|
||||
# Lux mainnet/testnet/local, and for a sovereign L1 it equals that chain's
|
||||
# EVM id, so one id names one network everywhere.
|
||||
network:
|
||||
id: 1
|
||||
# Inline genesis JSON. Empty uses the build's own genesis for network.id.
|
||||
genesis: ""
|
||||
# Chain configs, keyed by chain alias: chains.C is written to C/config.json.
|
||||
chains: {}
|
||||
|
||||
ports:
|
||||
http: 9630
|
||||
staking: 9631
|
||||
|
||||
db: zapdb
|
||||
|
||||
# Extra luxd flags, as a list of "--flag=value".
|
||||
args: []
|
||||
|
||||
storage:
|
||||
size: 200Gi
|
||||
class: ""
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: "2"
|
||||
memory: 8Gi
|
||||
limits:
|
||||
memory: 16Gi
|
||||
|
||||
# Paths are the node's own probe endpoints. Readiness reports bootstrap and
|
||||
# router state; liveness reports only that the process serves, so a node that
|
||||
# is still catching up is left alone instead of being restarted.
|
||||
probes:
|
||||
readiness: /v1/health/ops/readiness
|
||||
liveness: /v1/health/ops/liveness
|
||||
# Bootstrapping a large chain takes a while — the startup probe grants that
|
||||
# time, and liveness only starts counting once it passes.
|
||||
startupSeconds: 1800
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
annotations: {}
|
||||
|
||||
budget:
|
||||
# One validator may be unavailable at a time; consensus keeps its margin.
|
||||
maxUnavailable: 1
|
||||
|
||||
serviceAccount:
|
||||
create: true
|
||||
name: ""
|
||||
annotations: {}
|
||||
|
||||
podAnnotations: {}
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
Reference in New Issue
Block a user