-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(kurtosis-devnet): use inspect as a data source
This enables us to retrieve information about artifacts and services, for downstream consumption.
- Loading branch information
Showing
2 changed files
with
341 additions
and
0 deletions.
There are no files selected for viewing
165 changes: 165 additions & 0 deletions
165
kurtosis-devnet/pkg/kurtosis/sources/inspect/inspect.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
package inspect | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"strings" | ||
) | ||
|
||
type PortMap map[string]int | ||
|
||
type ServiceMap map[string]PortMap | ||
|
||
// InspectData represents the parsed output of "kurtosis enclave inspect" | ||
type InspectData struct { | ||
FileArtifacts []string | ||
UserServices ServiceMap | ||
} | ||
|
||
type Inspector struct{} | ||
|
||
type InspectorOption func(*Inspector) | ||
|
||
func NewInspector(opts ...InspectorOption) *Inspector { | ||
e := &Inspector{} | ||
for _, opt := range opts { | ||
opt(e) | ||
} | ||
return e | ||
} | ||
|
||
// extractPortName extracts the port name from the left part of a port mapping | ||
func extractPortName(leftPart string) string { | ||
if strings.Contains(leftPart, ":") { | ||
lastColonIndex := strings.LastIndex(leftPart, ":") | ||
return strings.TrimSpace(leftPart[:lastColonIndex]) | ||
} | ||
|
||
fields := strings.Fields(leftPart) | ||
if len(fields) > 0 { | ||
return fields[0] | ||
} | ||
return "" | ||
} | ||
|
||
// extractPort extracts the port number from the right part of a port mapping | ||
// TODO: this is a bit of a hack, but it works for now. It'll probably break | ||
// once we start using the k8s backend, as the IPs will become important. | ||
func extractPort(rightPart string) (int, error) { | ||
rightPart = strings.TrimSpace(rightPart) | ||
rightPart = strings.TrimPrefix(rightPart, "http://") | ||
if !strings.HasPrefix(rightPart, "127.0.0.1:") { | ||
return 0, fmt.Errorf("invalid port mapping format") | ||
} | ||
|
||
portStr := strings.TrimPrefix(rightPart, "127.0.0.1:") | ||
var port int | ||
_, err := fmt.Sscanf(portStr, "%d", &port) | ||
return port, err | ||
} | ||
|
||
// parsePortMapping parses a port mapping string and adds it to the result | ||
func parsePortMapping(line string, currentService string, result *InspectData) { | ||
parts := strings.Split(line, "->") | ||
if len(parts) < 2 { | ||
return | ||
} | ||
|
||
leftPart := strings.TrimRight(parts[0], " \t") | ||
|
||
portName := extractPortName(leftPart) | ||
if portName == "" { | ||
return | ||
} | ||
|
||
port, err := extractPort(parts[1]) | ||
if err == nil && currentService != "" { | ||
result.UserServices[currentService][portName] = port | ||
} | ||
} | ||
|
||
// ExtractData parses the output of "kurtosis enclave inspect" command | ||
// TODO: we DEFINITELY need to move this to kurtosis SDK. | ||
func (e *Inspector) ExtractData(r io.Reader) (*InspectData, error) { | ||
result := &InspectData{ | ||
FileArtifacts: make([]string, 0), | ||
UserServices: make(ServiceMap), | ||
} | ||
|
||
scanner := bufio.NewScanner(r) | ||
|
||
// States for parsing different sections | ||
const ( | ||
None = iota | ||
Files | ||
Services | ||
) | ||
|
||
state := None | ||
var currentService string | ||
|
||
for scanner.Scan() { | ||
line := scanner.Text() | ||
// Only trim for section detection | ||
trimmedLine := strings.TrimSpace(line) | ||
|
||
if trimmedLine == "" { | ||
continue | ||
} | ||
|
||
// Check section headers using trimmed line | ||
if strings.Contains(trimmedLine, "Files Artifacts") { | ||
state = Files | ||
continue | ||
} | ||
if strings.Contains(trimmedLine, "User Services") { | ||
state = Services | ||
continue | ||
} | ||
|
||
// Skip header lines | ||
if strings.HasPrefix(trimmedLine, "UUID") || strings.HasPrefix(trimmedLine, "====") { | ||
continue | ||
} | ||
|
||
switch state { | ||
case Files: | ||
fields := strings.Fields(trimmedLine) | ||
if len(fields) >= 2 { | ||
result.FileArtifacts = append(result.FileArtifacts, fields[1]) | ||
} | ||
|
||
case Services: | ||
fields := strings.Fields(trimmedLine) | ||
if len(fields) == 0 { | ||
continue | ||
} | ||
|
||
// If line starts with UUID, it's a new service | ||
if len(fields) >= 2 && len(fields[0]) == 12 { | ||
currentService = fields[1] | ||
result.UserServices[currentService] = make(map[string]int) | ||
|
||
// Check if there's a port mapping on the same line | ||
if strings.Contains(line, "->") { | ||
// Find the position after the service name | ||
serviceNameEnd := strings.Index(line, currentService) + len(currentService) | ||
// Process the rest of the line for port mapping | ||
portLine := line[serviceNameEnd:] | ||
if strings.Contains(portLine, "->") { | ||
parsePortMapping(portLine, currentService, result) | ||
} | ||
} | ||
} else if strings.Contains(line, "->") { | ||
parsePortMapping(line, currentService, result) | ||
} | ||
} | ||
} | ||
|
||
if err := scanner.Err(); err != nil { | ||
return nil, fmt.Errorf("error scanning output: %w", err) | ||
} | ||
|
||
return result, nil | ||
} |
176 changes: 176 additions & 0 deletions
176
kurtosis-devnet/pkg/kurtosis/sources/inspect/inspect_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
package inspect | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestParseInspectOutput(t *testing.T) { | ||
output := `Name: interop-devnet | ||
UUID: 1aca207b7bfd | ||
Status: RUNNING | ||
Creation Time: Mon, 16 Dec 2024 21:43:28 CET | ||
Flags: | ||
========================================= Files Artifacts ========================================= | ||
UUID Name | ||
24fa22fbbe9e 1-lighthouse-geth-0-63 | ||
018a906c5ea5 el_cl_genesis_data | ||
7a52f4b6848f final-genesis-timestamp | ||
1dfce39e2be9 genesis-el-cl-env-file | ||
49805cf85754 genesis_validators_root | ||
02ea3e61386e jwt_file | ||
19d0b8addd06 keymanager_file | ||
233da3830dd2 op-deployer-configs | ||
15b859be0607 op-deployer-fund-script | ||
27127fc07627 op_jwt_fileop-kurtosis-1 | ||
b6740ec44fb2 op_jwt_fileop-kurtosis-2 | ||
5ce33ff4e9ef prysm-password | ||
550585a62aa7 validator-ranges | ||
========================================== User Services ========================================== | ||
UUID Name Ports Status | ||
295ece6f10b0 cl-1-lighthouse-geth http: 4000/tcp -> http://127.0.0.1:56397 RUNNING | ||
metrics: 5054/tcp -> http://127.0.0.1:56398 | ||
tcp-discovery: 9000/tcp -> 127.0.0.1:56399 | ||
udp-discovery: 9000/udp -> 127.0.0.1:50029 | ||
d8010602c8d9 el-1-geth-lighthouse engine-rpc: 8551/tcp -> 127.0.0.1:56384 RUNNING | ||
metrics: 9001/tcp -> http://127.0.0.1:56385 | ||
rpc: 8545/tcp -> 127.0.0.1:56382 | ||
tcp-discovery: 30303/tcp -> 127.0.0.1:56381 | ||
udp-discovery: 30303/udp -> 127.0.0.1:50818 | ||
ws: 8546/tcp -> 127.0.0.1:56383 | ||
cea9c515cc61 op-batcher-op-kurtosis-1 http: 8548/tcp -> http://127.0.0.1:56772 RUNNING | ||
0d0dea3a7281 op-batcher-op-kurtosis-2 http: 8548/tcp -> http://127.0.0.1:57052 RUNNING | ||
108409b50fc1 op-cl-1-op-node-op-geth-op-kurtosis-1 http: 8547/tcp -> http://127.0.0.1:56752 RUNNING | ||
tcp-discovery: 9003/tcp -> 127.0.0.1:56753 | ||
udp-discovery: 9003/udp -> 127.0.0.1:61159 | ||
a5392ca8849f op-cl-1-op-node-op-geth-op-kurtosis-2 http: 8547/tcp -> http://127.0.0.1:56901 RUNNING | ||
tcp-discovery: 9003/tcp -> 127.0.0.1:56902 | ||
udp-discovery: 9003/udp -> 127.0.0.1:58904 | ||
93128de6641b op-el-1-op-geth-op-node-op-kurtosis-1 engine-rpc: 8551/tcp -> 127.0.0.1:56734 RUNNING | ||
metrics: 9001/tcp -> 127.0.0.1:56735 | ||
rpc: 8545/tcp -> http://127.0.0.1:56732 | ||
tcp-discovery: 30303/tcp -> 127.0.0.1:56731 | ||
udp-discovery: 30303/udp -> 127.0.0.1:64848 | ||
ws: 8546/tcp -> 127.0.0.1:56733 | ||
884fca1b00ad op-el-1-op-geth-op-node-op-kurtosis-2 engine-rpc: 8551/tcp -> 127.0.0.1:56786 RUNNING | ||
metrics: 9001/tcp -> 127.0.0.1:56787 | ||
rpc: 8545/tcp -> http://127.0.0.1:56784 | ||
tcp-discovery: 30303/tcp -> 127.0.0.1:56783 | ||
udp-discovery: 30303/udp -> 127.0.0.1:52005 | ||
ws: 8546/tcp -> 127.0.0.1:56785 | ||
a75ce8815bea validator-key-generation-cl-validator-keystore <none> RUNNING | ||
155a7d9a065d vc-1-geth-lighthouse metrics: 8080/tcp -> http://127.0.0.1:56408 RUNNING | ||
` | ||
|
||
result, err := NewInspector().ExtractData(strings.NewReader(output)) | ||
if err != nil { | ||
t.Fatalf("Failed to parse inspect output: %v", err) | ||
} | ||
|
||
// Verify file artifacts | ||
expectedFiles := []string{ | ||
"1-lighthouse-geth-0-63", | ||
"el_cl_genesis_data", | ||
"final-genesis-timestamp", | ||
"genesis-el-cl-env-file", | ||
"genesis_validators_root", | ||
"jwt_file", | ||
"keymanager_file", | ||
"op-deployer-configs", | ||
"op-deployer-fund-script", | ||
"op_jwt_fileop-kurtosis-1", | ||
"op_jwt_fileop-kurtosis-2", | ||
"prysm-password", | ||
"validator-ranges", | ||
} | ||
|
||
if len(result.FileArtifacts) != len(expectedFiles) { | ||
t.Errorf("Expected %d file artifacts, got %d", len(expectedFiles), len(result.FileArtifacts)) | ||
} | ||
|
||
for i, file := range expectedFiles { | ||
if i >= len(result.FileArtifacts) { | ||
t.Errorf("Missing expected file artifact: %s", file) | ||
continue | ||
} | ||
if result.FileArtifacts[i] != file { | ||
t.Errorf("Expected file artifact %s, got %s", file, result.FileArtifacts[i]) | ||
} | ||
} | ||
|
||
// Verify services and ports | ||
expectedServices := map[string]map[string]int{ | ||
"cl-1-lighthouse-geth": { | ||
"http": 56397, | ||
"metrics": 56398, | ||
"tcp-discovery": 56399, | ||
"udp-discovery": 50029, | ||
}, | ||
"el-1-geth-lighthouse": { | ||
"engine-rpc": 56384, | ||
"metrics": 56385, | ||
"rpc": 56382, | ||
"tcp-discovery": 56381, | ||
"udp-discovery": 50818, | ||
"ws": 56383, | ||
}, | ||
"op-batcher-op-kurtosis-1": { | ||
"http": 56772, | ||
}, | ||
"op-batcher-op-kurtosis-2": { | ||
"http": 57052, | ||
}, | ||
"op-cl-1-op-node-op-geth-op-kurtosis-1": { | ||
"http": 56752, | ||
"tcp-discovery": 56753, | ||
"udp-discovery": 61159, | ||
}, | ||
"op-cl-1-op-node-op-geth-op-kurtosis-2": { | ||
"http": 56901, | ||
"tcp-discovery": 56902, | ||
"udp-discovery": 58904, | ||
}, | ||
"op-el-1-op-geth-op-node-op-kurtosis-1": { | ||
"engine-rpc": 56734, | ||
"metrics": 56735, | ||
"rpc": 56732, | ||
"tcp-discovery": 56731, | ||
"udp-discovery": 64848, | ||
"ws": 56733, | ||
}, | ||
"op-el-1-op-geth-op-node-op-kurtosis-2": { | ||
"engine-rpc": 56786, | ||
"metrics": 56787, | ||
"rpc": 56784, | ||
"tcp-discovery": 56783, | ||
"udp-discovery": 52005, | ||
"ws": 56785, | ||
}, | ||
"validator-key-generation-cl-validator-keystore": {}, | ||
"vc-1-geth-lighthouse": { | ||
"metrics": 56408, | ||
}, | ||
} | ||
|
||
for service, expectedPorts := range expectedServices { | ||
ports, exists := result.UserServices[service] | ||
if !exists { | ||
t.Errorf("Expected service %s not found", service) | ||
continue | ||
} | ||
|
||
for portName, expectedPort := range expectedPorts { | ||
actualPort, exists := ports[portName] | ||
if !exists { | ||
t.Errorf("Expected port %s not found for service %s", portName, service) | ||
continue | ||
} | ||
if actualPort != expectedPort { | ||
t.Errorf("For service %s port %s: expected port %d, got %d", | ||
service, portName, expectedPort, actualPort) | ||
} | ||
} | ||
} | ||
} |