Skip to content

Commit 0c223a4

Browse files
authored
feat: signet-node (#2)
1 parent 0823d0a commit 0c223a4

File tree

7 files changed

+1240
-0
lines changed

7 files changed

+1240
-0
lines changed

pkg/signet_node/constants.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package signet_node
2+
3+
// Resource defaults
4+
const (
5+
// Storage defaults
6+
DefaultStorageSize = "150Gi"
7+
DefaultStorageClass = "aws-gp3"
8+
9+
// StatefulSet defaults
10+
DefaultReplicas = 1
11+
12+
// Resource allocation defaults
13+
DefaultCPULimit = "2"
14+
DefaultMemoryLimit = "16Gi"
15+
DefaultCPURequest = "2"
16+
DefaultMemoryRequest = "4Gi"
17+
18+
// Port defaults
19+
MetricsPort = 9001
20+
RpcPort = 8545
21+
WsPort = 8546
22+
AuthRpcPort = 8551
23+
DiscoveryPort = 30303
24+
ConsensusHttpPort = 4000
25+
ConsensusMetricsPort = 5054
26+
HostIpcPort = 8547
27+
RollupHttpPort = 8645
28+
RollupWsPort = 8646
29+
30+
// Component name
31+
ComponentKind = "the-builder:index:SignetNode"
32+
)
33+
34+
// Resource name suffixes
35+
const (
36+
ServiceSuffix = "-service"
37+
StatefulSetSuffix = "-set"
38+
ConfigMapSuffix = "-configmap"
39+
PvcSuffix = "-data"
40+
SecretSuffix = "-secret"
41+
VirtualServiceSuffix = "-vservice"
42+
)
43+
44+
// Resource names
45+
const (
46+
SignetNodeName = "signet-node"
47+
LighthouseName = "lighthouse"
48+
ExecutionJwtName = "execution-jwt"
49+
RollupDataName = "rollup-data"
50+
ExecutionConfigName = "exex-configmap"
51+
)

pkg/signet_node/helpers.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package signet_node
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"strings"
7+
"unicode"
8+
9+
corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1"
10+
metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1"
11+
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
12+
)
13+
14+
// CreatePersistentVolumeClaim creates a new PVC with the given name and size
15+
func CreatePersistentVolumeClaim(
16+
ctx *pulumi.Context,
17+
name string,
18+
namespace pulumi.StringInput,
19+
storageSize pulumi.StringInput,
20+
storageClass string,
21+
component pulumi.Resource,
22+
) (*corev1.PersistentVolumeClaim, error) {
23+
if storageSize == nil {
24+
storageSize = pulumi.String(DefaultStorageSize)
25+
}
26+
27+
if storageClass == "" {
28+
storageClass = DefaultStorageClass
29+
}
30+
31+
pvc, err := corev1.NewPersistentVolumeClaim(ctx, name, &corev1.PersistentVolumeClaimArgs{
32+
Metadata: &metav1.ObjectMetaArgs{
33+
Name: pulumi.String(name),
34+
Labels: pulumi.StringMap{
35+
"app.kubernetes.io/name": pulumi.String(name),
36+
"app.kubernetes.io/part-of": pulumi.String(name),
37+
},
38+
Namespace: namespace,
39+
},
40+
Spec: &corev1.PersistentVolumeClaimSpecArgs{
41+
AccessModes: pulumi.StringArray{pulumi.String("ReadWriteOnce")},
42+
Resources: &corev1.VolumeResourceRequirementsArgs{
43+
Requests: pulumi.StringMap{
44+
"storage": storageSize,
45+
},
46+
},
47+
StorageClassName: pulumi.String(storageClass),
48+
},
49+
}, pulumi.Parent(component))
50+
51+
if err != nil {
52+
return nil, fmt.Errorf("failed to create PVC %s: %w", name, err)
53+
}
54+
55+
return pvc, nil
56+
}
57+
58+
// CreateResourceLabels creates a consistent set of Kubernetes labels for resources
59+
func CreateResourceLabels(name string) pulumi.StringMap {
60+
return pulumi.StringMap{
61+
"app": pulumi.String(name),
62+
"app.kubernetes.io/name": pulumi.String(name),
63+
"app.kubernetes.io/part-of": pulumi.String(name),
64+
}
65+
}
66+
67+
// CreateEnvironmentVars creates environment variables from the SignetNodeEnv struct
68+
func CreateEnvironmentVars(env SignetNodeEnv) corev1.EnvVarArray {
69+
result := corev1.EnvVarArray{}
70+
71+
// Process all string inputs from the struct's tags
72+
envVarMap := GetEnvironmentVarsFromStruct(env)
73+
for name, value := range envVarMap {
74+
result = append(result, &corev1.EnvVarArgs{
75+
Name: pulumi.String(name),
76+
Value: value.(pulumi.StringInput),
77+
})
78+
}
79+
80+
return result
81+
}
82+
83+
// GetEnvironmentVarsFromStruct uses reflection to extract environment variables from struct tags
84+
func GetEnvironmentVarsFromStruct(env SignetNodeEnv) map[string]pulumi.Input {
85+
result := make(map[string]pulumi.Input)
86+
87+
t := reflect.TypeOf(env)
88+
v := reflect.ValueOf(env)
89+
90+
for i := 0; i < t.NumField(); i++ {
91+
field := t.Field(i)
92+
93+
// Get the field value
94+
fieldValue := v.Field(i).Interface()
95+
96+
// Skip nil values
97+
if fieldValue == nil {
98+
continue
99+
}
100+
101+
// Convert camelCase to SNAKE_CASE for env var name
102+
envName := CamelToSnake(field.Name)
103+
104+
// Add to map
105+
result[envName] = fieldValue.(pulumi.Input)
106+
}
107+
108+
return result
109+
}
110+
111+
// CamelToSnake converts a camelCase string to SNAKE_CASE
112+
func CamelToSnake(s string) string {
113+
var result strings.Builder
114+
115+
// Handle consecutive uppercase characters (acronyms)
116+
for i, r := range s {
117+
if unicode.IsUpper(r) {
118+
// Add underscore if not the first character and either:
119+
// 1. Previous character is lowercase, or
120+
// 2. Not the last character and next character is lowercase (end of acronym)
121+
needsUnderscore := i > 0 && (unicode.IsLower(rune(s[i-1])) ||
122+
(i < len(s)-1 && unicode.IsLower(rune(s[i+1])) && i > 1 && unicode.IsUpper(rune(s[i-1]))))
123+
124+
if needsUnderscore {
125+
result.WriteRune('_')
126+
}
127+
result.WriteRune(unicode.ToUpper(r))
128+
} else {
129+
result.WriteRune(unicode.ToUpper(r))
130+
}
131+
}
132+
return result.String()
133+
}
134+
135+
// GetResourceName returns a consistent name for a resource
136+
func GetResourceName(baseName string, resourceType string) string {
137+
switch resourceType {
138+
case "service":
139+
return fmt.Sprintf("%s%s", baseName, ServiceSuffix)
140+
case "statefulset":
141+
return fmt.Sprintf("%s%s", baseName, StatefulSetSuffix)
142+
case "configmap":
143+
return fmt.Sprintf("%s%s", baseName, ConfigMapSuffix)
144+
case "pvc":
145+
return fmt.Sprintf("%s%s", baseName, PvcSuffix)
146+
case "secret":
147+
return fmt.Sprintf("%s%s", baseName, SecretSuffix)
148+
case "virtualservice":
149+
return fmt.Sprintf("%s%s", baseName, VirtualServiceSuffix)
150+
default:
151+
return baseName
152+
}
153+
}
154+
155+
// ResourceRequirements returns consistent resource requirements for pods
156+
func NewResourceRequirements(cpuLimit, memoryLimit, cpuRequest, memoryRequest string) *corev1.ResourceRequirementsArgs {
157+
if cpuLimit == "" {
158+
cpuLimit = DefaultCPULimit
159+
}
160+
if memoryLimit == "" {
161+
memoryLimit = DefaultMemoryLimit
162+
}
163+
if cpuRequest == "" {
164+
cpuRequest = DefaultCPURequest
165+
}
166+
if memoryRequest == "" {
167+
memoryRequest = DefaultMemoryRequest
168+
}
169+
170+
return &corev1.ResourceRequirementsArgs{
171+
Limits: pulumi.StringMap{
172+
"cpu": pulumi.String(cpuLimit),
173+
"memory": pulumi.String(memoryLimit),
174+
},
175+
Requests: pulumi.StringMap{
176+
"cpu": pulumi.String(cpuRequest),
177+
"memory": pulumi.String(memoryRequest),
178+
},
179+
}
180+
}

pkg/signet_node/helpers_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package signet_node
2+
3+
import (
4+
"testing"
5+
6+
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestCamelToSnake(t *testing.T) {
11+
tests := []struct {
12+
input string
13+
expected string
14+
}{
15+
{"HostZenithAddress", "HOST_ZENITH_ADDRESS"},
16+
{"RpcPort", "RPC_PORT"},
17+
{"SignetChainId", "SIGNET_CHAIN_ID"},
18+
{"BaseFeeRecipient", "BASE_FEE_RECIPIENT"},
19+
{"HostStartTimestamp", "HOST_START_TIMESTAMP"},
20+
}
21+
22+
for _, test := range tests {
23+
t.Run(test.input, func(t *testing.T) {
24+
result := CamelToSnake(test.input)
25+
assert.Equal(t, test.expected, result)
26+
})
27+
}
28+
}
29+
30+
func TestGetEnvironmentVarsFromStruct(t *testing.T) {
31+
env := SignetNodeEnv{
32+
HostZenithAddress: pulumi.String("0x123"),
33+
RpcPort: pulumi.String("8545"),
34+
WsRpcPort: pulumi.String("8546"),
35+
}
36+
37+
result := GetEnvironmentVarsFromStruct(env)
38+
39+
assert.NotNil(t, result["HOST_ZENITH_ADDRESS"])
40+
assert.NotNil(t, result["RPC_PORT"])
41+
assert.NotNil(t, result["WS_RPC_PORT"])
42+
assert.Equal(t, 3, len(result))
43+
}
44+
45+
func TestCreateResourceLabels(t *testing.T) {
46+
name := "test-resource"
47+
labels := CreateResourceLabels(name)
48+
49+
assert.Equal(t, pulumi.String(name), labels["app"])
50+
assert.Equal(t, pulumi.String(name), labels["app.kubernetes.io/name"])
51+
assert.Equal(t, pulumi.String(name), labels["app.kubernetes.io/part-of"])
52+
}
53+
54+
func TestGetResourceName(t *testing.T) {
55+
tests := []struct {
56+
baseName string
57+
resourceType string
58+
expected string
59+
}{
60+
{"signet-node", "service", "signet-node-service"},
61+
{"lighthouse", "statefulset", "lighthouse-set"},
62+
{"execution", "configmap", "execution-configmap"},
63+
{"data", "pvc", "data-data"},
64+
{"jwt", "secret", "jwt-secret"},
65+
}
66+
67+
for _, test := range tests {
68+
t.Run(test.resourceType, func(t *testing.T) {
69+
result := GetResourceName(test.baseName, test.resourceType)
70+
assert.Equal(t, test.expected, result)
71+
})
72+
}
73+
}
74+
75+
func TestResourceRequirements(t *testing.T) {
76+
// Test with default values
77+
resources := NewResourceRequirements("", "", "", "")
78+
79+
// Check that resources is not nil
80+
assert.NotNil(t, resources)
81+
82+
// Verify the structure indirectly by checking key presence
83+
stringMap := resources.Limits.(pulumi.StringMap)
84+
assert.Contains(t, stringMap, "cpu")
85+
assert.Contains(t, stringMap, "memory")
86+
assert.Equal(t, pulumi.String(DefaultCPULimit), stringMap["cpu"])
87+
assert.Equal(t, pulumi.String(DefaultMemoryLimit), stringMap["memory"])
88+
89+
requestsMap := resources.Requests.(pulumi.StringMap)
90+
assert.Contains(t, requestsMap, "cpu")
91+
assert.Contains(t, requestsMap, "memory")
92+
assert.Equal(t, pulumi.String(DefaultCPURequest), requestsMap["cpu"])
93+
assert.Equal(t, pulumi.String(DefaultMemoryRequest), requestsMap["memory"])
94+
95+
// Test with custom values
96+
customResources := NewResourceRequirements("4", "32Gi", "2", "16Gi")
97+
98+
customLimitsMap := customResources.Limits.(pulumi.StringMap)
99+
assert.Equal(t, pulumi.String("4"), customLimitsMap["cpu"])
100+
assert.Equal(t, pulumi.String("32Gi"), customLimitsMap["memory"])
101+
102+
customRequestsMap := customResources.Requests.(pulumi.StringMap)
103+
assert.Equal(t, pulumi.String("2"), customRequestsMap["cpu"])
104+
assert.Equal(t, pulumi.String("16Gi"), customRequestsMap["memory"])
105+
}

0 commit comments

Comments
 (0)