Skip to content

Commit 135c538

Browse files
authored
feat: pylon package (#15)
* fix: fixes found from actually trying to use this code (#27) * fix: unused constants * fix: better validation
1 parent b91dd75 commit 135c538

File tree

9 files changed

+658
-0
lines changed

9 files changed

+658
-0
lines changed

pkg/ethereum/ethereum_node.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import (
1010

1111
// NewEthereumNodeComponent creates a new Ethereum node component that combines an execution client and a consensus client
1212
func NewEthereumNodeComponent(ctx *pulumi.Context, args *EthereumNodeArgs, opts ...pulumi.ResourceOption) (*EthereumNodeComponent, error) {
13+
if err := args.Validate(); err != nil {
14+
return nil, fmt.Errorf("invalid ethereum node args: %w", err)
15+
}
16+
1317
component := &EthereumNodeComponent{
1418
Name: args.Name,
1519
Namespace: args.Namespace,

pkg/ethereum/validation.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package ethereum
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
// Validate validates the EthereumNodeArgs struct
8+
func (args *EthereumNodeArgs) Validate() error {
9+
if args.Name == "" {
10+
return fmt.Errorf("name is required")
11+
}
12+
13+
if args.Namespace == "" {
14+
return fmt.Errorf("namespace is required")
15+
}
16+
17+
if args.ExecutionClient == nil {
18+
return fmt.Errorf("executionClient is required")
19+
}
20+
21+
if args.ConsensusClient == nil {
22+
return fmt.Errorf("consensusClient is required")
23+
}
24+
25+
// Validate execution client
26+
if err := args.ExecutionClient.Validate(); err != nil {
27+
return fmt.Errorf("execution client validation failed: %w", err)
28+
}
29+
30+
// Validate consensus client
31+
if err := args.ConsensusClient.Validate(); err != nil {
32+
return fmt.Errorf("consensus client validation failed: %w", err)
33+
}
34+
35+
return nil
36+
}

pkg/ethereum/validation_test.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package ethereum
2+
3+
import (
4+
"testing"
5+
6+
"github.com/init4tech/signet-infra-components/pkg/ethereum/consensus"
7+
"github.com/init4tech/signet-infra-components/pkg/ethereum/execution"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestEthereumNodeArgsValidate(t *testing.T) {
12+
// Test with valid args
13+
validArgs := EthereumNodeArgs{
14+
Name: "test-node",
15+
Namespace: "default",
16+
ExecutionClient: &execution.ExecutionClientArgs{
17+
Name: "test-execution",
18+
Namespace: "default",
19+
StorageSize: "100Gi",
20+
StorageClass: "standard",
21+
Image: "test-execution-image",
22+
ImagePullPolicy: "Always",
23+
JWTSecret: "test-jwt-secret",
24+
P2PPort: 30303,
25+
RPCPort: 8545,
26+
WSPort: 8546,
27+
MetricsPort: 9090,
28+
AuthRPCPort: 8551,
29+
DiscoveryPort: 30303,
30+
},
31+
ConsensusClient: &consensus.ConsensusClientArgs{
32+
Name: "test-consensus",
33+
Namespace: "default",
34+
StorageSize: "100Gi",
35+
StorageClass: "standard",
36+
Image: "test-consensus-image",
37+
ImagePullPolicy: "Always",
38+
JWTSecret: "test-jwt-secret",
39+
P2PPort: 30303,
40+
BeaconAPIPort: 5052,
41+
MetricsPort: 9090,
42+
ExecutionClientEndpoint: "http://execution:8551",
43+
},
44+
}
45+
46+
err := validArgs.Validate()
47+
assert.NoError(t, err)
48+
49+
// Test with missing name
50+
invalidArgs1 := EthereumNodeArgs{
51+
Namespace: "default",
52+
ExecutionClient: validArgs.ExecutionClient,
53+
ConsensusClient: validArgs.ConsensusClient,
54+
}
55+
56+
err = invalidArgs1.Validate()
57+
assert.Error(t, err)
58+
assert.Contains(t, err.Error(), "name is required")
59+
60+
// Test with missing namespace
61+
invalidArgs2 := EthereumNodeArgs{
62+
Name: "test-node",
63+
ExecutionClient: validArgs.ExecutionClient,
64+
ConsensusClient: validArgs.ConsensusClient,
65+
}
66+
67+
err = invalidArgs2.Validate()
68+
assert.Error(t, err)
69+
assert.Contains(t, err.Error(), "namespace is required")
70+
71+
// Test with missing execution client
72+
invalidArgs3 := EthereumNodeArgs{
73+
Name: "test-node",
74+
Namespace: "default",
75+
ConsensusClient: validArgs.ConsensusClient,
76+
}
77+
78+
err = invalidArgs3.Validate()
79+
assert.Error(t, err)
80+
assert.Contains(t, err.Error(), "executionClient is required")
81+
82+
// Test with missing consensus client
83+
invalidArgs4 := EthereumNodeArgs{
84+
Name: "test-node",
85+
Namespace: "default",
86+
ExecutionClient: validArgs.ExecutionClient,
87+
}
88+
89+
err = invalidArgs4.Validate()
90+
assert.Error(t, err)
91+
assert.Contains(t, err.Error(), "consensusClient is required")
92+
93+
// Test with invalid execution client
94+
invalidExecutionClient := &execution.ExecutionClientArgs{
95+
// Missing required fields
96+
}
97+
invalidArgs5 := EthereumNodeArgs{
98+
Name: "test-node",
99+
Namespace: "default",
100+
ExecutionClient: invalidExecutionClient,
101+
ConsensusClient: validArgs.ConsensusClient,
102+
}
103+
104+
err = invalidArgs5.Validate()
105+
assert.Error(t, err)
106+
assert.Contains(t, err.Error(), "execution client validation failed")
107+
108+
// Test with invalid consensus client
109+
invalidConsensusClient := &consensus.ConsensusClientArgs{
110+
// Missing required fields
111+
}
112+
invalidArgs6 := EthereumNodeArgs{
113+
Name: "test-node",
114+
Namespace: "default",
115+
ExecutionClient: validArgs.ExecutionClient,
116+
ConsensusClient: invalidConsensusClient,
117+
}
118+
119+
err = invalidArgs6.Validate()
120+
assert.Error(t, err)
121+
assert.Contains(t, err.Error(), "consensus client validation failed")
122+
}

pkg/pylon/constants.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package pylon
2+
3+
// Storage constants
4+
const (
5+
ExecutionClientStorageSize = "150Gi"
6+
ConsensusClientStorageSize = "100Gi"
7+
StorageClassAWSGP3 = "aws-gp3"
8+
)
9+
10+
// Port constants
11+
const (
12+
ExecutionP2PPort = 30303
13+
ExecutionRPCPort = 8545
14+
ExecutionWSPort = 8546
15+
ExecutionMetricsPort = 9001
16+
ExecutionAuthRPCPort = 8551
17+
ConsensusBeaconAPIPort = 4000
18+
ConsensusMetricsPort = 5054
19+
)
20+
21+
// Image constants
22+
const (
23+
ConsensusClientImage = "sigp/lighthouse:latest"
24+
ImagePullPolicyAlways = "Always"
25+
)

pkg/pylon/helpers.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package pylon

pkg/pylon/pylon.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package pylon
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/init4tech/signet-infra-components/pkg/ethereum"
7+
"github.com/init4tech/signet-infra-components/pkg/ethereum/consensus"
8+
"github.com/init4tech/signet-infra-components/pkg/ethereum/execution"
9+
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/s3"
10+
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
11+
)
12+
13+
func NewPylonComponent(ctx *pulumi.Context, args *PylonComponentArgs, opts ...pulumi.ResourceOption) (*PylonComponent, error) {
14+
if err := args.Validate(); err != nil {
15+
return nil, fmt.Errorf("invalid pylon component args: %w", err)
16+
}
17+
18+
component := &PylonComponent{}
19+
20+
err := ctx.RegisterComponentResource("signet:index:Pylon", args.Name, component, opts...)
21+
if err != nil {
22+
return nil, err
23+
}
24+
25+
// Convert public args to internal args
26+
internalArgs := args.toInternal()
27+
28+
stack := ctx.Stack()
29+
30+
// Get the existing Route53 hosted zone for signet.sh
31+
dbProjectName := internalArgs.DbProjectName
32+
dbStackName := fmt.Sprintf("%s/%s", dbProjectName, stack)
33+
34+
// TODO: this should be a stack reference to the pylon db stack- should this be an arg?
35+
// Need to think about how i want to handle the separation of the pylon db and pylon components
36+
thePylonDbStack, err := pulumi.NewStackReference(ctx, dbStackName, nil)
37+
if err != nil {
38+
return nil, err
39+
}
40+
41+
// Get the database cluster endpoint (unused for now but needed for future implementation)
42+
_ = thePylonDbStack.GetStringOutput(pulumi.String("dbClusterEndpoint"))
43+
44+
// Create the S3 bucket for blob storage (unused for now but needed for future implementation)
45+
_, err = s3.NewBucketV2(ctx, "pylon-blob-bucket", &s3.BucketV2Args{
46+
Bucket: internalArgs.PylonBlobBucketName,
47+
})
48+
if err != nil {
49+
return nil, err
50+
}
51+
52+
// Convert environment to internal type for use with ethereum components
53+
internalEnv := args.Env.toInternal()
54+
55+
// Create Ethereum node component
56+
ethereumNodeArgs := &ethereum.EthereumNodeArgs{
57+
Name: args.Name,
58+
Namespace: args.Namespace,
59+
ExecutionClient: &execution.ExecutionClientArgs{
60+
Name: args.Name,
61+
Namespace: args.Namespace,
62+
StorageSize: ExecutionClientStorageSize,
63+
StorageClass: StorageClassAWSGP3,
64+
Image: args.PylonImage,
65+
JWTSecret: args.ExecutionJwt,
66+
P2PPort: ExecutionP2PPort,
67+
RPCPort: ExecutionRPCPort,
68+
WSPort: ExecutionWSPort,
69+
MetricsPort: ExecutionMetricsPort,
70+
AuthRPCPort: ExecutionAuthRPCPort,
71+
DiscoveryPort: ExecutionP2PPort,
72+
ExecutionClientEnv: internalEnv,
73+
},
74+
ConsensusClient: &consensus.ConsensusClientArgs{
75+
Name: args.Name,
76+
Namespace: args.Namespace,
77+
StorageSize: ConsensusClientStorageSize,
78+
StorageClass: StorageClassAWSGP3,
79+
Image: ConsensusClientImage,
80+
ImagePullPolicy: ImagePullPolicyAlways,
81+
BeaconAPIPort: ConsensusBeaconAPIPort,
82+
MetricsPort: ConsensusMetricsPort,
83+
},
84+
}
85+
86+
ethereumNode, err := ethereum.NewEthereumNodeComponent(ctx, ethereumNodeArgs, pulumi.Parent(component))
87+
if err != nil {
88+
return nil, err
89+
}
90+
component.EthereumNode = ethereumNode
91+
92+
return component, nil
93+
}

0 commit comments

Comments
 (0)