Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0cd67de
using {{.Field}} syntax in network helmfile templating
Padraic-O-Mhuiris Nov 24, 2025
d774a7f
added id for namespace suffix
Padraic-O-Mhuiris Nov 24, 2025
928fc4c
cleaner templating of just the values block
Padraic-O-Mhuiris Nov 24, 2025
b229380
split templating to just a values.yaml.gotmpl file
Padraic-O-Mhuiris Nov 24, 2025
b4a78be
added id to values.yaml and ensure namespace construction
Padraic-O-Mhuiris Nov 24, 2025
796c4a2
updated CLAUDE.md
Padraic-O-Mhuiris Nov 24, 2025
1e08bae
implemented basic sync command
Padraic-O-Mhuiris Nov 24, 2025
95346c1
enforcing helmfile as template
Padraic-O-Mhuiris Nov 24, 2025
3ba750b
fix ethereum helmfile
Padraic-O-Mhuiris Nov 24, 2025
8be47bf
added network delete
Padraic-O-Mhuiris Nov 24, 2025
2c8c094
feat: bump helm/helmfile versions and fix ethereum network svc discovery
bussyjd Dec 1, 2025
7d77269
fix: add ethereum-ingress release and fix lighthouse execution endpoi…
bussyjd Dec 1, 2025
0444b6b
replaced env vars with template field
Padraic-O-Mhuiris Dec 2, 2025
a7b882f
updated CLAUDE.md
Padraic-O-Mhuiris Dec 2, 2025
ed12de2
ensure field sort order
Padraic-O-Mhuiris Dec 2, 2025
ab16dba
added simple yaml validation
Padraic-O-Mhuiris Dec 2, 2025
0acd1fd
fix: add missing network flag to lighthouse and cleanup whitespace
bussyjd Dec 2, 2025
cdd676f
id improvements
Padraic-O-Mhuiris Dec 2, 2025
9226770
fix: remove trailing spaces and fix indentation
bussyjd Dec 2, 2025
c5da2d2
Merge pull request #113 from ObolNetwork/helm-bump
bussyjd Dec 2, 2025
0dcea71
update claude
Padraic-O-Mhuiris Dec 2, 2025
9b5a07b
feat: add standardized pod labels for network discovery (#115)
bussyjd Dec 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 172 additions & 97 deletions CLAUDE.md

Large diffs are not rendered by default.

105 changes: 65 additions & 40 deletions cmd/obol/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"slices"
"strings"

"github.com/ObolNetwork/obol-stack/internal/config"
Expand Down Expand Up @@ -36,22 +37,27 @@ func networkCommand(cfg *config.Config) *cli.Command {
},
},
{
Name: "delete",
Usage: "Remove network and clean up cluster resources",
ArgsUsage: "<network>",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "force",
Aliases: []string{"f"},
Usage: "Skip confirmation prompt",
},
Name: "sync",
Usage: "Deploy or update network configuration to cluster",
ArgsUsage: "<network>/<id> or <network>-<id>",
Action: func(c *cli.Context) error {
if c.NArg() == 0 {
return fmt.Errorf("deployment identifier required (e.g., ethereum/knowing-wahoo or ethereum-knowing-wahoo)")
}
deploymentIdentifier := c.Args().First()
return network.Sync(cfg, deploymentIdentifier)
},
},
{
Name: "delete",
Usage: "Remove network deployment and clean up cluster resources",
ArgsUsage: "<network>/<id> or <network>-<id>",
Action: func(c *cli.Context) error {
if c.NArg() == 0 {
return fmt.Errorf("network name required (e.g., ethereum, helios)")
return fmt.Errorf("deployment identifier required (e.g., ethereum/test-deploy or ethereum-test-deploy)")
}
networkName := c.Args().First()
return network.Delete(cfg, networkName, c.Bool("force"))
deploymentIdentifier := c.Args().First()
return network.Delete(cfg, deploymentIdentifier)
},
},
},
Expand All @@ -68,76 +74,95 @@ func buildNetworkInstallCommands(cfg *config.Config) []*cli.Command {

var commands []*cli.Command
for _, networkName := range networks {
// Parse the embedded helmfile to get env vars
envVars, err := network.ParseEmbeddedNetworkEnvVars(networkName)
// Parse the embedded values template to get fields
fields, err := network.ParseTemplateFields(networkName)
if err != nil {
// Skip networks we can't parse
continue
}

// Build flags from env vars
flags := []cli.Flag{}
for _, envVar := range envVars {
// Build flags from template fields
flags := []cli.Flag{
// id flag is always present (special case - not parsed from template)
&cli.StringFlag{
Name: "id",
Usage: fmt.Sprintf("Deployment identifier for namespace (e.g., 'my-node' becomes '%s-my-node', defaults to generated petname)", networkName),
Required: false,
},
// force flag to allow overwriting existing deployments
&cli.BoolFlag{
Name: "force",
Aliases: []string{"f"},
Usage: "Overwrite existing deployment configuration if it already exists",
},
}

// Add flags from parsed template fields
for _, field := range fields {
// Build usage string
usage := envVar.Description
usage := field.Description
if usage == "" {
usage = fmt.Sprintf("Override %s", envVar.Name)
usage = fmt.Sprintf("Override %s", field.Name)
}

// Mark as required if no default value
if envVar.Required {
if field.Required {
usage = "[REQUIRED] " + usage
}

// Add enum options if available
if len(envVar.EnumValues) > 0 {
usage += fmt.Sprintf(" [options: %s]", strings.Join(envVar.EnumValues, ", "))
if len(field.EnumValues) > 0 {
usage += fmt.Sprintf(" [options: %s]", strings.Join(field.EnumValues, ", "))
}

// Add default value
if envVar.DefaultValue != "" {
usage += fmt.Sprintf(" (default: %s)", envVar.DefaultValue)
if field.DefaultValue != "" {
usage += fmt.Sprintf(" (default: %s)", field.DefaultValue)
}

flags = append(flags, &cli.StringFlag{
Name: envVar.FlagName,
Name: field.FlagName,
Usage: usage,
Required: envVar.Required,
Required: field.Required,
})
}

// Create the network-specific install command
netName := networkName // Capture for closure
netEnvVars := envVars // Capture for validation
netFields := fields // Capture for validation
commands = append(commands, &cli.Command{
Name: netName,
Usage: fmt.Sprintf("Install %s network", netName),
Flags: flags,
Action: func(c *cli.Context) error {
// Collect and validate flag values
overrides := make(map[string]string)
for _, envVar := range netEnvVars {
value := c.String(envVar.FlagName)

// Collect id flag (special case - not in parsed fields)
if idValue := c.String("id"); idValue != "" {
overrides["id"] = idValue
}

// Collect parsed template fields
for _, field := range netFields {
value := c.String(field.FlagName)
if value != "" {
// Validate enum constraint if defined
if len(envVar.EnumValues) > 0 {
valid := false
for _, enumVal := range envVar.EnumValues {
if value == enumVal {
valid = true
break
}
}
if len(field.EnumValues) > 0 {
valid := slices.Contains(field.EnumValues, value)
if !valid {
return fmt.Errorf("invalid value '%s' for --%s. Valid options: %s",
value, envVar.FlagName, strings.Join(envVar.EnumValues, ", "))
value, field.FlagName, strings.Join(field.EnumValues, ", "))
}
}
overrides[envVar.FlagName] = value
overrides[field.FlagName] = value
}
}

return network.Install(cfg, netName, overrides)
// Get force flag
force := c.Bool("force")

return network.Install(cfg, netName, overrides, force)
},
})
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ go 1.25

require (
github.com/dustinkirkland/golang-petname v0.0.0-20240428194347-eebcea082ee0
github.com/google/uuid v1.6.0
github.com/urfave/cli/v2 v2.27.7
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/dustinkirkland/golang-petname v0.0.0-20240428194347-eebcea082ee0 h1:aYo8nnk3ojoQkP5iErif5Xxv0Mo0Ga/FR5+ffl/7+Nk=
github.com/dustinkirkland/golang-petname v0.0.0-20240428194347-eebcea082ee0/go.mod h1:8AuBTZBRSFqEYBPYULd+NN474/zZBLP+6WeT5S9xlAc=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
33 changes: 9 additions & 24 deletions internal/embed/networks/aztec/helmfile.yaml.gotmpl
Original file line number Diff line number Diff line change
@@ -1,25 +1,10 @@
values:
# @enum mainnet
# @description Blockchain network to deploy
- network: {{ env "AZTEC_NETWORK" | default "mainnet" }}
# @description Attester private key (hex string)
attesterPrivateKey: {{ env "AZTEC_ATTESTER_PRIVATE_KEY" }}
# @description L1 Execution RPC URL override (defaults to local erpc endpoint based on network)
l1ExecutionUrl: {{ env "AZTEC_L1_EXECUTION_URL" | default "" }}
# @description L1 Consensus RPC URL
l1ConsensusUrl: {{ env "AZTEC_L1_CONSENSUS_URL" | default "https://ethereum-beacon-api.publicnode.com" }}
# Computed value: L1 Execution URL - uses override or defaults to erpc endpoint
__l1ExecutionUrl: {{ if (env "AZTEC_L1_EXECUTION_URL") }}{{ env "AZTEC_L1_EXECUTION_URL" }}{{ else }}http://erpc.erpc.svc.cluster.local:4000/rpc/{{ env "AZTEC_NETWORK" | default "mainnet" }}{{ end }}

---

repositories:
- name: obol
url: https://obolnetwork.github.io/helm-charts/

releases:
- name: aztec-sequencer
namespace: aztec
namespace: aztec-{{ .Values.id }}
createNamespace: true
chart: obol/aztec-node
version: 0.2.0
Expand All @@ -45,20 +30,20 @@ releases:
- --network
- '{{ .Values.network }}'
l1ExecutionUrls:
- '{{ .Values.__l1ExecutionUrl }}'
- '{{ if .Values.l1ExecutionUrl }}{{ .Values.l1ExecutionUrl }}{{ else }}http://erpc.erpc.svc.cluster.local:4000/rpc/{{ .Values.network }}{{ end }}'
l1ConsensusUrls:
- '{{ .Values.l1ConsensusUrl }}'
resources:
requests:
cpu: "4"
memory: "16Gi"
cpu: "4"
memory: "16Gi"
limits:
cpu: "8"
memory: "32Gi"
cpu: "8"
memory: "32Gi"
storage:
dataDirectory: /data
dataStoreMapSize: "134217728"
worldStateMapSize: "134217728"
dataStoreMapSize: "134217728"
worldStateMapSize: "134217728"
startupProbe:
periodSeconds: 60
failureThreshold: 60
Expand All @@ -74,7 +59,7 @@ releases:
httpPort: 8080
p2p:
enabled: true
nodePortEnabled: false
nodePortEnabled: false
port: 40400
announcePort: 40400
admin:
Expand Down
18 changes: 18 additions & 0 deletions internal/embed/networks/aztec/values.yaml.gotmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Configuration via CLI flags
# Template fields populated by obol CLI during network installation

# @enum mainnet
# @default mainnet
# @description Blockchain network to deploy
network: {{.Network}}

# @description Attester private key (hex string)
attesterPrivateKey: {{.AttesterPrivateKey}}

# @default
# @description L1 Execution RPC URL (defaults to ERPC: http://erpc.erpc.svc.cluster.local:4000/rpc/{network})
l1ExecutionUrl: {{.L1ExecutionUrl}}

# @default https://ethereum-beacon-api.publicnode.com
# @description L1 Consensus RPC URL
l1ConsensusUrl: {{.L1ConsensusUrl}}
57 changes: 39 additions & 18 deletions internal/embed/networks/ethereum/helmfile.yaml.gotmpl
Original file line number Diff line number Diff line change
@@ -1,39 +1,25 @@
# Configuration via environment variables
# Override with: ETHEREUM_NETWORK, ETHEREUM_EXECUTION_CLIENT, ETHEREUM_CONSENSUS_CLIENT
values:
# @enum mainnet,sepolia,holesky,hoodi
# @description Blockchain network to deploy
- network: {{ env "ETHEREUM_NETWORK" | default "mainnet" }}
# @enum reth,geth,nethermind,besu,erigon,ethereumjs
# @description Execution layer client
executionClient: {{ env "ETHEREUM_EXECUTION_CLIENT" | default "reth" }}
# @enum lighthouse,prysm,teku,nimbus,lodestar,grandine
# @description Consensus layer client
consensusClient: {{ env "ETHEREUM_CONSENSUS_CLIENT" | default "lighthouse" }}

---

repositories:
- name: ethereum-helm-charts
url: https://ethpandaops.github.io/ethereum-helm-charts

releases:
# Create PVCs first - before the Ethereum node
- name: ethereum-pvcs
namespace: ethereum
namespace: ethereum-{{ .Values.id }}
createNamespace: true
chart: .
values:
# Pass only the values needed for PVC creation
- executionClient: '{{ .Values.executionClient }}'
- id: '{{ .Values.id }}'
executionClient: '{{ .Values.executionClient }}'
consensusClient: '{{ .Values.consensusClient }}'
network: '{{ .Values.network }}'

# Ethereum node (execution and consensus clients)
# Uses the external ethereum-helm-charts/ethereum-node umbrella chart
# Depends on ethereum-pvcs to ensure PVCs exist first
- name: ethereum
namespace: ethereum
namespace: ethereum-{{ .Values.id }}
createNamespace: true
chart: ethereum-helm-charts/ethereum-node
needs: [ethereum-pvcs]
Expand All @@ -54,6 +40,18 @@ releases:
- {{ .Values.executionClient }}:
enabled: true
nameOverride: execution-{{ .Values.executionClient }}-{{ .Values.network }}
labels:
app.kubernetes.io/part-of: obol.stack
obol.stack/chain: ethereum
obol.stack/network: {{ .Values.network }}
obol.stack/type: execution
obol.stack/client: {{ .Values.executionClient }}
podLabels:
app.kubernetes.io/part-of: obol.stack
obol.stack/chain: ethereum
obol.stack/network: {{ .Values.network }}
obol.stack/type: execution
obol.stack/client: {{ .Values.executionClient }}
persistence:
enabled: true
size: 500Gi
Expand All @@ -64,9 +62,32 @@ releases:
- {{ .Values.consensusClient }}:
enabled: true
nameOverride: consensus-{{ .Values.consensusClient }}-{{ .Values.network }}
labels:
app.kubernetes.io/part-of: obol.stack
obol.stack/chain: ethereum
obol.stack/network: {{ .Values.network }}
obol.stack/type: consensus
obol.stack/client: {{ .Values.consensusClient }}
podLabels:
app.kubernetes.io/part-of: obol.stack
obol.stack/chain: ethereum
obol.stack/network: {{ .Values.network }}
obol.stack/type: consensus
obol.stack/client: {{ .Values.consensusClient }}
extraArgs:
- --execution-endpoint=http://ethereum-execution-{{ .Values.executionClient }}-{{ .Values.network }}:8551
- --network={{ .Values.network }}
persistence:
enabled: true
size: 200Gi
# Use existing PVC with network/client naming: consensus-{client}-{network}
existingClaim: consensus-{{ .Values.consensusClient }}-{{ .Values.network }}

# Ingress for Ethereum node
- name: ethereum-ingress
namespace: ethereum-{{ .Values.id }}
chart: .
values:
- executionClient: {{ .Values.executionClient }}
consensusClient: {{ .Values.consensusClient }}
network: {{ .Values.network }}
4 changes: 2 additions & 2 deletions internal/embed/networks/ethereum/templates/ingress.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ spec:
pathType: ImplementationSpecific
backend:
service:
name: ethereum-execution
name: ethereum-execution-{{ .Values.executionClient }}-{{ .Values.network }}
port:
number: 8545
- path: /ethereum/beacon(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: ethereum-beacon
name: ethereum-consensus-{{ .Values.consensusClient }}-{{ .Values.network }}
port:
number: 5052
{{- end }}
Loading