Commit 604fd605 authored by Zach Howard's avatar Zach Howard Committed by GitHub

kt-devnet: improves post-deployment output for nodes/services (#13707)

parent c1d9e420
package kurtosis package kurtosis
import ( import (
"fmt"
"strconv" "strconv"
"strings" "strings"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/inspect" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/inspect"
) )
// ServiceFinder is the main entry point for finding service endpoints // ServiceFinder is the main entry point for finding services and their endpoints
type ServiceFinder struct { type ServiceFinder struct {
services inspect.ServiceMap services inspect.ServiceMap
nodeServices []string nodeServices []string
interestingPorts []string l2ServicePrefix string
l2ServicePrefix string
} }
// ServiceFinderOption configures a ServiceFinder // ServiceFinderOption configures a ServiceFinder
...@@ -26,13 +24,6 @@ func WithNodeServices(services []string) ServiceFinderOption { ...@@ -26,13 +24,6 @@ func WithNodeServices(services []string) ServiceFinderOption {
} }
} }
// WithInterestingPorts sets the ports to look for
func WithInterestingPorts(ports []string) ServiceFinderOption {
return func(f *ServiceFinder) {
f.interestingPorts = ports
}
}
// WithL2ServicePrefix sets the prefix used to identify L2 services // WithL2ServicePrefix sets the prefix used to identify L2 services
func WithL2ServicePrefix(prefix string) ServiceFinderOption { func WithL2ServicePrefix(prefix string) ServiceFinderOption {
return func(f *ServiceFinder) { return func(f *ServiceFinder) {
...@@ -43,10 +34,9 @@ func WithL2ServicePrefix(prefix string) ServiceFinderOption { ...@@ -43,10 +34,9 @@ func WithL2ServicePrefix(prefix string) ServiceFinderOption {
// NewServiceFinder creates a new ServiceFinder with the given options // NewServiceFinder creates a new ServiceFinder with the given options
func NewServiceFinder(services inspect.ServiceMap, opts ...ServiceFinderOption) *ServiceFinder { func NewServiceFinder(services inspect.ServiceMap, opts ...ServiceFinderOption) *ServiceFinder {
f := &ServiceFinder{ f := &ServiceFinder{
services: services, services: services,
nodeServices: []string{"cl", "el"}, nodeServices: []string{"cl", "el"},
interestingPorts: []string{"rpc", "http"}, l2ServicePrefix: "op-",
l2ServicePrefix: "op-",
} }
for _, opt := range opts { for _, opt := range opts {
opt(f) opt(f)
...@@ -54,8 +44,8 @@ func NewServiceFinder(services inspect.ServiceMap, opts ...ServiceFinderOption) ...@@ -54,8 +44,8 @@ func NewServiceFinder(services inspect.ServiceMap, opts ...ServiceFinderOption)
return f return f
} }
// FindL1Endpoints finds L1 nodes. Currently returns empty endpoints as specified. // FindL1Services finds L1 nodes.
func (f *ServiceFinder) FindL1Endpoints() ([]Node, EndpointMap) { func (f *ServiceFinder) FindL1Services() ([]Node, ServiceMap) {
return f.findRPCEndpoints(func(serviceName string) (string, int, bool) { return f.findRPCEndpoints(func(serviceName string) (string, int, bool) {
// Only match services that start with one of the node service identifiers. // Only match services that start with one of the node service identifiers.
// We might have to change this if we need to support L1 services beyond nodes. // We might have to change this if we need to support L1 services beyond nodes.
...@@ -69,8 +59,8 @@ func (f *ServiceFinder) FindL1Endpoints() ([]Node, EndpointMap) { ...@@ -69,8 +59,8 @@ func (f *ServiceFinder) FindL1Endpoints() ([]Node, EndpointMap) {
}) })
} }
// FindL2Endpoints finds L2 nodes and endpoints for a specific network // FindL2Services finds L2 nodes and services for a specific network
func (f *ServiceFinder) FindL2Endpoints(network string) ([]Node, EndpointMap) { func (f *ServiceFinder) FindL2Services(network string) ([]Node, ServiceMap) {
networkSuffix := "-" + network networkSuffix := "-" + network
return f.findRPCEndpoints(func(serviceName string) (string, int, bool) { return f.findRPCEndpoints(func(serviceName string) (string, int, bool) {
if strings.HasSuffix(serviceName, networkSuffix) { if strings.HasSuffix(serviceName, networkSuffix) {
...@@ -83,47 +73,44 @@ func (f *ServiceFinder) FindL2Endpoints(network string) ([]Node, EndpointMap) { ...@@ -83,47 +73,44 @@ func (f *ServiceFinder) FindL2Endpoints(network string) ([]Node, EndpointMap) {
} }
// findRPCEndpoints looks for services matching the given predicate that have an RPC port // findRPCEndpoints looks for services matching the given predicate that have an RPC port
func (f *ServiceFinder) findRPCEndpoints(matchService func(string) (string, int, bool)) ([]Node, EndpointMap) { func (f *ServiceFinder) findRPCEndpoints(matchService func(string) (string, int, bool)) ([]Node, ServiceMap) {
endpointMap := make(EndpointMap) serviceMap := make(ServiceMap)
var nodes []Node var nodes []Node
for serviceName, ports := range f.services { for serviceName, ports := range f.services {
var portInfo *inspect.PortInfo
for _, interestingPort := range f.interestingPorts {
if p, ok := ports[interestingPort]; ok {
portInfo = &p
break
}
}
if portInfo == nil {
continue
}
if serviceIdentifier, num, ok := matchService(serviceName); ok { if serviceIdentifier, num, ok := matchService(serviceName); ok {
var allocated bool var allocated bool
for _, service := range f.nodeServices { for _, service := range f.nodeServices {
if serviceIdentifier == service { if serviceIdentifier == service {
if num > len(nodes) { if num > len(nodes) {
nodes = append(nodes, make(Node)) nodes = append(nodes, Node{
Services: make(ServiceMap),
})
} }
host := portInfo.Host endpoints := make(EndpointMap)
if host == "" { for portName, portInfo := range ports {
host = "localhost" endpoints[portName] = portInfo
}
nodes[num-1].Services[serviceIdentifier] = Service{
Name: serviceName,
Endpoints: endpoints,
} }
nodes[num-1][serviceIdentifier] = fmt.Sprintf("http://%s:%d", host, portInfo.Port)
allocated = true allocated = true
} }
} }
if !allocated { if !allocated {
host := portInfo.Host endpoints := make(EndpointMap)
if host == "" { for portName, portInfo := range ports {
host = "localhost" endpoints[portName] = portInfo
}
serviceMap[serviceIdentifier] = Service{
Name: serviceName,
Endpoints: endpoints,
} }
endpointMap[serviceIdentifier] = fmt.Sprintf("http://%s:%d", host, portInfo.Port)
} }
} }
} }
return nodes, endpointMap return nodes, serviceMap
} }
// serviceTag returns the shorthand service tag and index if it's a service with multiple instances // serviceTag returns the shorthand service tag and index if it's a service with multiple instances
......
...@@ -50,40 +50,84 @@ func TestFindRPCEndpoints(t *testing.T) { ...@@ -50,40 +50,84 @@ func TestFindRPCEndpoints(t *testing.T) {
} }
tests := []struct { tests := []struct {
name string name string
services inspect.ServiceMap services inspect.ServiceMap
findFn func(*ServiceFinder) ([]Node, EndpointMap) findFn func(*ServiceFinder) ([]Node, ServiceMap)
wantNodes []Node wantNodes []Node
wantEndpoints EndpointMap wantServices ServiceMap
}{ }{
{ {
name: "find L1 endpoints", name: "find L1 endpoints",
services: testServices, services: testServices,
findFn: func(f *ServiceFinder) ([]Node, EndpointMap) { findFn: func(f *ServiceFinder) ([]Node, ServiceMap) {
return f.FindL1Endpoints() return f.FindL1Services()
}, },
wantNodes: []Node{ wantNodes: []Node{
{ {
"cl": "http://localhost:52693", Services: ServiceMap{
"el": "http://localhost:52645", "cl": Service{
Name: "cl-1-lighthouse-geth",
Endpoints: EndpointMap{
"metrics": {Port: 52691},
"tcp-discovery": {Port: 52692},
"udp-discovery": {Port: 58275},
"http": {Port: 52693},
},
},
"el": Service{
Name: "el-1-geth-lighthouse",
Endpoints: EndpointMap{
"metrics": {Port: 52643},
"tcp-discovery": {Port: 52644},
"udp-discovery": {Port: 51936},
"engine-rpc": {Port: 52642},
"rpc": {Port: 52645},
"ws": {Port: 52646},
},
},
},
}, },
}, },
wantEndpoints: EndpointMap{}, wantServices: ServiceMap{},
}, },
{ {
name: "find op-kurtosis L2 endpoints", name: "find op-kurtosis L2 endpoints",
services: testServices, services: testServices,
findFn: func(f *ServiceFinder) ([]Node, EndpointMap) { findFn: func(f *ServiceFinder) ([]Node, ServiceMap) {
return f.FindL2Endpoints("op-kurtosis") return f.FindL2Services("op-kurtosis")
}, },
wantNodes: []Node{ wantNodes: []Node{
{ {
"cl": "http://localhost:53503", Services: ServiceMap{
"el": "http://localhost:53402", "cl": Service{
Name: "op-cl-1-op-node-op-geth-op-kurtosis",
Endpoints: EndpointMap{
"udp-discovery": {Port: 50990},
"http": {Port: 53503},
"tcp-discovery": {Port: 53504},
},
},
"el": Service{
Name: "op-el-1-op-geth-op-node-op-kurtosis",
Endpoints: EndpointMap{
"udp-discovery": {Port: 53233},
"engine-rpc": {Port: 53399},
"metrics": {Port: 53400},
"tcp-discovery": {Port: 53401},
"rpc": {Port: 53402},
"ws": {Port: 53403},
},
},
},
}, },
}, },
wantEndpoints: EndpointMap{ wantServices: ServiceMap{
"batcher": "http://localhost:53572", "batcher": Service{
Name: "op-batcher-op-kurtosis",
Endpoints: EndpointMap{
"http": {Port: 53572},
},
},
}, },
}, },
{ {
...@@ -93,12 +137,17 @@ func TestFindRPCEndpoints(t *testing.T) { ...@@ -93,12 +137,17 @@ func TestFindRPCEndpoints(t *testing.T) {
"http": {Host: "custom.host", Port: 8080}, "http": {Host: "custom.host", Port: 8080},
}, },
}, },
findFn: func(f *ServiceFinder) ([]Node, EndpointMap) { findFn: func(f *ServiceFinder) ([]Node, ServiceMap) {
return f.FindL2Endpoints("custom-host") return f.FindL2Services("custom-host")
}, },
wantNodes: nil, wantNodes: nil,
wantEndpoints: EndpointMap{ wantServices: ServiceMap{
"batcher": "http://custom.host:8080", "batcher": Service{
Name: "op-batcher-custom-host",
Endpoints: EndpointMap{
"http": {Host: "custom.host", Port: 8080},
},
},
}, },
}, },
} }
...@@ -106,9 +155,9 @@ func TestFindRPCEndpoints(t *testing.T) { ...@@ -106,9 +155,9 @@ func TestFindRPCEndpoints(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
finder := NewServiceFinder(tt.services) finder := NewServiceFinder(tt.services)
gotNodes, gotEndpoints := tt.findFn(finder) gotNodes, gotServices := tt.findFn(finder)
assert.Equal(t, tt.wantNodes, gotNodes) assert.Equal(t, tt.wantNodes, gotNodes)
assert.Equal(t, tt.wantEndpoints, gotEndpoints) assert.Equal(t, tt.wantServices, gotServices)
}) })
} }
} }
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/run" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/run"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/wrappers" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/wrappers"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/deployer" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/deployer"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/inspect"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec"
) )
...@@ -18,14 +19,23 @@ const ( ...@@ -18,14 +19,23 @@ const (
DefaultEnclave = "devnet" DefaultEnclave = "devnet"
) )
type EndpointMap map[string]string type EndpointMap map[string]inspect.PortInfo
type Node = EndpointMap type ServiceMap map[string]Service
type Service struct {
Name string `json:"name"`
Endpoints EndpointMap `json:"endpoints"`
}
type Node struct {
Services ServiceMap `json:"services"`
}
type Chain struct { type Chain struct {
Name string `json:"name"` Name string `json:"name"`
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
Services EndpointMap `json:"services,omitempty"` Services ServiceMap `json:"services,omitempty"`
Nodes []Node `json:"nodes"` Nodes []Node `json:"nodes"`
Addresses deployer.DeploymentAddresses `json:"addresses,omitempty"` Addresses deployer.DeploymentAddresses `json:"addresses,omitempty"`
Wallets WalletMap `json:"wallets,omitempty"` Wallets WalletMap `json:"wallets,omitempty"`
...@@ -169,10 +179,10 @@ func (d *KurtosisDeployer) GetEnvironmentInfo(ctx context.Context, spec *spec.En ...@@ -169,10 +179,10 @@ func (d *KurtosisDeployer) GetEnvironmentInfo(ctx context.Context, spec *spec.En
// Find L1 endpoint // Find L1 endpoint
finder := NewServiceFinder(inspectResult.UserServices) finder := NewServiceFinder(inspectResult.UserServices)
if nodes, endpoints := finder.FindL1Endpoints(); len(nodes) > 0 { if nodes, services := finder.FindL1Services(); len(nodes) > 0 {
chain := &Chain{ chain := &Chain{
Name: "Ethereum", Name: "Ethereum",
Services: endpoints, Services: services,
Nodes: nodes, Nodes: nodes,
} }
if deployerState.State != nil { if deployerState.State != nil {
...@@ -184,12 +194,12 @@ func (d *KurtosisDeployer) GetEnvironmentInfo(ctx context.Context, spec *spec.En ...@@ -184,12 +194,12 @@ func (d *KurtosisDeployer) GetEnvironmentInfo(ctx context.Context, spec *spec.En
// Find L2 endpoints // Find L2 endpoints
for _, chainSpec := range spec.Chains { for _, chainSpec := range spec.Chains {
nodes, endpoints := finder.FindL2Endpoints(chainSpec.Name) nodes, services := finder.FindL2Services(chainSpec.Name)
chain := &Chain{ chain := &Chain{
Name: chainSpec.Name, Name: chainSpec.Name,
ID: chainSpec.NetworkID, ID: chainSpec.NetworkID,
Services: endpoints, Services: services,
Nodes: nodes, Nodes: nodes,
} }
......
...@@ -8,8 +8,8 @@ import ( ...@@ -8,8 +8,8 @@ import (
// PortInfo contains the host and port number for a service port // PortInfo contains the host and port number for a service port
type PortInfo struct { type PortInfo struct {
Host string Host string `json:"host"`
Port int Port int `json:"port"`
} }
type PortMap map[string]PortInfo type PortMap map[string]PortInfo
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment