Skip to content

Commit 948b9c2

Browse files
authored
Merge pull request #84 from ObolNetwork/feature/obol-agent
Obol-agent template
2 parents 9b2aafc + 83abd2c commit 948b9c2

File tree

12 files changed

+453
-25
lines changed

12 files changed

+453
-25
lines changed

CLAUDE.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,15 +151,15 @@ obol
151151
2. Parses each network's `helmfile.yaml.gotmpl` for environment variable annotations
152152
3. Generates CLI flags automatically from annotations:
153153
```yaml
154-
# @enum mainnet,sepolia,holesky,hoodi
154+
# @enum mainnet,sepolia,hoodi
155155
# @default mainnet
156156
# @description Blockchain network to deploy
157157
- network: {{.Network}}
158158
```
159159
Becomes: `--network` flag with enum validation and default value
160160

161161
**Network install flow**:
162-
1. User runs: `obol network install ethereum --network=holesky --execution-client=geth`
162+
1. User runs: `obol network install ethereum --network=hoodi --execution-client=geth`
163163
2. CLI collects flag values into `overrides` map
164164
3. Validates enum constraints
165165
4. Calls `network.Install(cfg, "ethereum", overrides)`
@@ -245,7 +245,7 @@ networks/
245245
246246
`values.yaml.gotmpl` contains configuration fields with annotations:
247247
```yaml
248-
# @enum mainnet,sepolia,holesky,hoodi
248+
# @enum mainnet,sepolia,hoodi
249249
# @default mainnet
250250
# @description Blockchain network to deploy
251251
network: {{.Network}}
@@ -322,15 +322,15 @@ obol network install ethereum --id prod --network=mainnet
322322
323323
# Multiple deployments with different configs
324324
obol network install ethereum --id mainnet-01
325-
obol network install ethereum --id holesky-test --network=holesky
325+
obol network install ethereum --id hoodi-test --network=hoodi
326326
# Both run simultaneously, isolated in separate namespaces
327327
```
328328

329329
### Network Configuration Flow
330330

331331
1. **Install** (config generation only):
332332
```
333-
obol network install ethereum --network=holesky --execution-client=geth --id my-node
333+
obol network install ethereum --network=hoodi --execution-client=geth --id my-node
334334
335335
Check if directory exists: ~/.config/obol/networks/ethereum/my-node/ (fail unless --force)
336336
@@ -727,7 +727,7 @@ obol network list
727727
obol network install ethereum --help
728728
729729
# Install with specific config
730-
obol network install ethereum --network=holesky --execution-client=geth
730+
obol network install ethereum --network=hoodi --execution-client=geth
731731
732732
# Verify deployment
733733
obol kubectl get namespaces | grep ethereum

README.md

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ obol network install ethereum
113113
obol network sync ethereum/nervous-otter
114114

115115
# Install another network configuration
116-
obol network install ethereum --network=holesky
116+
obol network install ethereum --network=hoodi
117117
# This creates a separate deployment like: ethereum-happy-panda
118118

119119
# Deploy the second network
@@ -200,23 +200,17 @@ obol network install ethereum --network=mainnet --execution-client=geth --consen
200200
# Deploy to cluster
201201
obol network sync ethereum/nervous-otter
202202

203-
# Install Holesky testnet with Reth + Lighthouse
204-
obol network install ethereum --network=holesky --execution-client=reth --consensus-client=lighthouse
205-
# Creates configuration: ethereum-laughing-elephant
203+
# Install Hoodi testnet with Reth + Lighthouse
204+
obol network install ethereum --network=hoodi --execution-client=reth --consensus-client=lighthouse
205+
# Creates: ethereum-laughing-elephant namespace
206206

207-
# Deploy Holesky to cluster
208-
obol network sync ethereum/laughing-elephant
209-
210-
# Install another Holesky instance for testing
211-
obol network install ethereum --network=holesky
212-
# Creates configuration: ethereum-happy-panda
213-
214-
# Deploy second Holesky instance
215-
obol network sync ethereum/happy-panda
207+
# Install another Hoodi instance for testing
208+
obol network install ethereum --network=hoodi
209+
# Creates: ethereum-happy-panda namespace
216210
```
217211

218212
**Ethereum configuration options:**
219-
- `--network`: Choose network (mainnet, sepolia, holesky, hoodi)
213+
- `--network`: Choose network (mainnet, sepolia, hoodi)
220214
- `--execution-client`: Choose execution client (reth, geth, nethermind, besu, erigon, ethereumjs)
221215
- `--consensus-client`: Choose consensus client (lighthouse, prysm, teku, nimbus, lodestar, grandine)
222216

@@ -708,7 +702,7 @@ The stack will include [eRPC](https://erpc.cloud/), a specialized Ethereum load
708702

709703
Network deployments will register their endpoints with ERPC, enabling seamless access to blockchain data across all deployed instances. For example:
710704
- `http://erpc.defaults.svc.cluster.local/ethereum/mainnet` → routes to mainnet deployment
711-
- `http://erpc.defaults.svc.cluster.local/ethereum/holesky` → routes to holesky deployment
705+
- `http://erpc.defaults.svc.cluster.local/ethereum/hoodi` → routes to hoodi deployment
712706

713707
### Advanced Tooling
714708

cmd/obol/main.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"path/filepath"
99
"syscall"
1010

11+
"github.com/ObolNetwork/obol-stack/internal/agent"
1112
"github.com/ObolNetwork/obol-stack/internal/app"
1213
"github.com/ObolNetwork/obol-stack/internal/config"
1314
"github.com/ObolNetwork/obol-stack/internal/stack"
@@ -43,7 +44,8 @@ COMMANDS:
4344
stack up Start the Obol Stack
4445
stack down Stop the Obol Stack
4546
stack purge Delete stack config (use --force to also delete data)
46-
47+
Obol Agent:
48+
agent init Initialize the Obol Agent with an API key
4749
Network Management:
4850
network list List available networks
4951
network install Install and deploy network to cluster
@@ -130,6 +132,31 @@ GLOBAL OPTIONS:
130132
},
131133
},
132134
// ============================================================
135+
// Obol Agent Commands
136+
// ============================================================
137+
{
138+
Name: "agent",
139+
Usage: "Manage Obol Agent",
140+
Subcommands: []*cli.Command{
141+
{
142+
Name: "init",
143+
Usage: "Initialize the Obol Agent with an API key",
144+
Flags: []cli.Flag{
145+
&cli.StringFlag{
146+
Name: "agent-api-key",
147+
Aliases: []string{"a"},
148+
Usage: "API key for the Obol Agent",
149+
EnvVars: []string{"AGENT_API_KEY"},
150+
},
151+
},
152+
Action: func(c *cli.Context) error {
153+
agentAPIKey := c.String("agent-api-key")
154+
return agent.Init(cfg, agentAPIKey)
155+
},
156+
},
157+
},
158+
},
159+
// ============================================================
133160
// Kubernetes Tool Passthroughs (with auto-configured KUBECONFIG)
134161
// ============================================================
135162
{

internal/agent/agent.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package agent
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"os"
7+
"os/exec"
8+
"path/filepath"
9+
"strings"
10+
11+
"github.com/ObolNetwork/obol-stack/internal/config"
12+
"github.com/ObolNetwork/obol-stack/internal/stack"
13+
)
14+
15+
const (
16+
kubeconfigFile = "kubeconfig.yaml"
17+
)
18+
19+
// Init initializes the Obol Agent with required secrets
20+
func Init(cfg *config.Config, agentAPIKey string) error {
21+
kubeconfigPath := filepath.Join(cfg.ConfigDir, kubeconfigFile)
22+
23+
// Check if kubeconfig exists (stack must be running)
24+
if _, err := os.Stat(kubeconfigPath); os.IsNotExist(err) {
25+
return fmt.Errorf("stack not running, use 'obol stack up' first")
26+
}
27+
28+
// Get stack ID for logging
29+
stackID := stack.GetStackID(cfg)
30+
if stackID == "" {
31+
return fmt.Errorf("stack ID not found, run 'obol stack init' first")
32+
}
33+
34+
// If no API key provided via flag, try to read from stdin
35+
if agentAPIKey == "" {
36+
stat, _ := os.Stdin.Stat()
37+
if (stat.Mode() & os.ModeCharDevice) == 0 {
38+
// Data is being piped to stdin
39+
data, err := io.ReadAll(os.Stdin)
40+
if err == nil {
41+
agentAPIKey = strings.TrimSpace(string(data))
42+
}
43+
}
44+
}
45+
46+
// Validate Agent API key was provided
47+
if agentAPIKey == "" {
48+
return fmt.Errorf("agent API key required via --agent-api-key flag or AGENT_API_KEY environment variable. Navigate to https://aistudio.google.com/api-keys to create an API key for your Obol Agent")
49+
}
50+
51+
fmt.Println("Initializing Obol Agent")
52+
fmt.Printf("Stack ID: %s\n", stackID)
53+
fmt.Println("Creating API key secret for Obol Agent")
54+
55+
kubectlPath := filepath.Join(cfg.BinDir, "kubectl")
56+
57+
// Create namespace (idempotent)
58+
nsCmd := exec.Command(kubectlPath, "--kubeconfig", kubeconfigPath, "create", "namespace", "agent", "--dry-run=client", "-o", "yaml")
59+
nsYAML, err := nsCmd.Output()
60+
if err != nil {
61+
return fmt.Errorf("failed to generate namespace manifest: %w", err)
62+
}
63+
64+
applyNs := exec.Command(kubectlPath, "--kubeconfig", kubeconfigPath, "apply", "-f", "-")
65+
applyNs.Stdin = strings.NewReader(string(nsYAML))
66+
applyNs.Stdout = os.Stdout
67+
applyNs.Stderr = os.Stderr
68+
if err := applyNs.Run(); err != nil {
69+
return fmt.Errorf("failed to create agent namespace: %w", err)
70+
}
71+
72+
// Create secret (idempotent)
73+
secretCmd := exec.Command(kubectlPath, "--kubeconfig", kubeconfigPath, "create", "secret", "generic", "obol-agent-api-key", "--from-literal=AGENT_API_KEY="+agentAPIKey, "--namespace=agent", "--dry-run=client", "-o", "yaml")
74+
secretYAML, err := secretCmd.Output()
75+
if err != nil {
76+
return fmt.Errorf("failed to generate secret manifest: %w", err)
77+
}
78+
79+
applySecret := exec.Command(kubectlPath, "--kubeconfig", kubeconfigPath, "apply", "-f", "-")
80+
applySecret.Stdin = strings.NewReader(string(secretYAML))
81+
applySecret.Stdout = os.Stdout
82+
applySecret.Stderr = os.Stderr
83+
if err := applySecret.Run(); err != nil {
84+
return fmt.Errorf("failed to create Agent API key secret: %w", err)
85+
}
86+
87+
fmt.Println("Agent API key secret created")
88+
fmt.Println("Obol Agent initialized successfully")
89+
90+
return nil
91+
}

0 commit comments

Comments
 (0)