Commit 90c18bcd authored by Yann Hodique's avatar Yann Hodique Committed by GitHub

feat(kurtosis-devnet): provide main wrapper for kurtosis (#13524)

This handles deploying the kurtosis enclave according to spec, and
then stitching together the relevant information to be transmitted for
downstream consumption.
parent cdf28e3d
set shell := ["/bin/bash", "-c"] set shell := ["/bin/bash", "-c"]
_kurtosis-run PACKAGE_NAME ARG_FILE ENCLAVE:
kurtosis run {{PACKAGE_NAME}} --args-file {{ARG_FILE}} --enclave {{ENCLAVE}} --show-enclave-inspect=false --image-download=missing
# Internal recipes for kurtosis-devnet # Internal recipes for kurtosis-devnet
_contracts-build BUNDLE='contracts-bundle.tar.gz': _contracts-build BUNDLE='contracts-bundle.tar.gz':
just ../packages/contracts-bedrock/forge-build just ../packages/contracts-bedrock/forge-build
......
package kurtosis
import (
"context"
"io"
"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"
)
type enclaveSpecAdapter struct{}
func (a *enclaveSpecAdapter) EnclaveSpec(r io.Reader) (*spec.EnclaveSpec, error) {
return spec.NewSpec().ExtractData(r)
}
var _ EnclaveSpecifier = (*enclaveSpecAdapter)(nil)
type enclaveInspectAdapter struct{}
func (a *enclaveInspectAdapter) EnclaveInspect(ctx context.Context, enclave string) (*inspect.InspectData, error) {
return inspect.NewInspector(enclave).ExtractData(ctx)
}
var _ EnclaveInspecter = (*enclaveInspectAdapter)(nil)
type enclaveDeployerAdapter struct{}
func (a *enclaveDeployerAdapter) EnclaveObserve(ctx context.Context, enclave string) (*deployer.DeployerData, error) {
return deployer.NewDeployer(enclave).ExtractData(ctx)
}
var _ EnclaveObserver = (*enclaveDeployerAdapter)(nil)
package kurtosis
import (
"context"
"io"
"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"
)
type EnclaveSpecifier interface {
EnclaveSpec(io.Reader) (*spec.EnclaveSpec, error)
}
type EnclaveInspecter interface {
EnclaveInspect(context.Context, string) (*inspect.InspectData, error)
}
type EnclaveObserver interface {
EnclaveObserve(context.Context, string) (*deployer.DeployerData, error)
}
package kurtosis
import (
"bytes"
"context"
"fmt"
"io"
"os"
"os/exec"
"strconv"
"strings"
"text/template"
"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"
)
const (
DefaultPackageName = "github.com/ethpandaops/optimism-package"
DefaultEnclave = "devnet"
)
type EndpointMap map[string]string
type Node = EndpointMap
type Chain struct {
Name string `json:"name"`
ID string `json:"id,omitempty"`
Services EndpointMap `json:"services,omitempty"`
Nodes []Node `json:"nodes"`
Addresses deployer.DeploymentAddresses `json:"addresses,omitempty"`
}
type Wallet struct {
Address string `json:"address"`
PrivateKey string `json:"private_key,omitempty"`
}
type WalletMap map[string]Wallet
// KurtosisEnvironment represents the output of a Kurtosis deployment
type KurtosisEnvironment struct {
L1 *Chain `json:"l1"`
L2 []*Chain `json:"l2"`
Wallets WalletMap `json:"wallets"`
}
// KurtosisDeployer handles deploying packages using Kurtosis
type KurtosisDeployer struct {
// Base directory where the deployment commands should be executed
baseDir string
// Template for the deployment command
cmdTemplate *template.Template
// Package name to deploy
packageName string
// Dry run mode
dryRun bool
// Enclave name
enclave string
enclaveSpec EnclaveSpecifier
enclaveInspecter EnclaveInspecter
enclaveObserver EnclaveObserver
}
const cmdTemplateStr = "just _kurtosis-run {{.PackageName}} {{.ArgFile}} {{.Enclave}}"
var defaultCmdTemplate *template.Template
func init() {
defaultCmdTemplate = template.Must(template.New("kurtosis_deploy_cmd").Parse(cmdTemplateStr))
}
type KurtosisDeployerOptions func(*KurtosisDeployer)
func WithKurtosisBaseDir(baseDir string) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) {
d.baseDir = baseDir
}
}
func WithKurtosisCmdTemplate(cmdTemplate *template.Template) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) {
d.cmdTemplate = cmdTemplate
}
}
func WithKurtosisPackageName(packageName string) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) {
d.packageName = packageName
}
}
func WithKurtosisDryRun(dryRun bool) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) {
d.dryRun = dryRun
}
}
func WithKurtosisEnclave(enclave string) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) {
d.enclave = enclave
}
}
func WithKurtosisEnclaveSpec(enclaveSpec EnclaveSpecifier) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) {
d.enclaveSpec = enclaveSpec
}
}
func WithKurtosisEnclaveInspecter(enclaveInspecter EnclaveInspecter) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) {
d.enclaveInspecter = enclaveInspecter
}
}
func WithKurtosisEnclaveObserver(enclaveObserver EnclaveObserver) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) {
d.enclaveObserver = enclaveObserver
}
}
// NewKurtosisDeployer creates a new KurtosisDeployer instance
func NewKurtosisDeployer(opts ...KurtosisDeployerOptions) *KurtosisDeployer {
d := &KurtosisDeployer{
baseDir: ".",
cmdTemplate: defaultCmdTemplate,
packageName: DefaultPackageName,
dryRun: false,
enclave: "devnet",
enclaveSpec: &enclaveSpecAdapter{},
enclaveInspecter: &enclaveInspectAdapter{},
enclaveObserver: &enclaveDeployerAdapter{},
}
for _, opt := range opts {
opt(d)
}
return d
}
// templateData holds the data for the command template
type templateData struct {
PackageName string
ArgFile string
Enclave string
}
// TODO: the following functions follow closely the naming convensions in place
// with optimism-package. We should probably make them a bit more generic, to
// make the whole thing less fragile.
// findRPCEndpoint looks for a service matching the given predicate that has an RPC port
func findRPCEndpoints(services inspect.ServiceMap, matchService func(string) (string, int, bool)) ([]Node, EndpointMap) {
interestingPorts := []string{"rpc", "http"}
nodeServices := []string{"cl", "el"}
endpointMap := make(EndpointMap)
var nodes []Node
for serviceName, ports := range services {
var port int
for _, interestingPort := range interestingPorts {
if p, ok := ports[interestingPort]; ok {
port = p
break
}
}
if port == 0 { // nothing to see here
continue
}
if serviceIdentifier, num, ok := matchService(serviceName); ok {
var allocated bool
for _, service := range nodeServices {
if serviceIdentifier == service { // this is a node
if num > len(nodes) {
nodes = append(nodes, make(Node))
}
nodes[num-1][serviceIdentifier] = fmt.Sprintf("http://localhost:%d", port)
allocated = true
}
}
if !allocated {
endpointMap[serviceIdentifier] = fmt.Sprintf("http://localhost:%d", port)
}
}
}
return nodes, endpointMap
}
// return the shorthand service tag (used as key in the final output) and the
// index if that's a service with multiple instances.
func serviceTag(serviceName string) (string, int) {
// Find index of first number
i := strings.IndexFunc(serviceName, func(r rune) bool {
return r >= '0' && r <= '9'
})
if i == -1 {
return serviceName, 0
}
idx, err := strconv.Atoi(serviceName[i : i+1])
if err != nil {
return serviceName, 0
}
return serviceName[:i-1], idx
}
const l2ServiceTagPrefix = "op-"
func findL2Endpoints(services inspect.ServiceMap, suffix string) ([]Node, EndpointMap) {
return findRPCEndpoints(services, func(serviceName string) (string, int, bool) {
if strings.HasSuffix(serviceName, suffix) {
name := strings.TrimSuffix(serviceName, suffix)
tag, idx := serviceTag(strings.TrimPrefix(name, l2ServiceTagPrefix))
return tag, idx, true
}
return "", 0, false
})
}
// TODO: L1 services are detected as "non-L2" right now. That might need to change
// in the future, but for now it's good enough.
func findL1Endpoints(services inspect.ServiceMap) ([]Node, EndpointMap) {
return findRPCEndpoints(services, func(serviceName string) (string, int, bool) {
match := !strings.HasPrefix(serviceName, l2ServiceTagPrefix)
if match {
tag, idx := serviceTag(serviceName)
return tag, idx, true
}
return "", 0, false
})
}
// prepareArgFile creates a temporary file with the input content and returns its path
// The caller is responsible for deleting the file.
func (d *KurtosisDeployer) prepareArgFile(input io.Reader) (string, error) {
argFile, err := os.CreateTemp("", "kurtosis-args-*.yaml")
if err != nil {
return "", fmt.Errorf("failed to create temporary arg file: %w", err)
}
defer argFile.Close()
if _, err := io.Copy(argFile, input); err != nil {
os.Remove(argFile.Name())
return "", fmt.Errorf("failed to write arg file: %w", err)
}
return argFile.Name(), nil
}
// runKurtosisCommand executes the kurtosis command with the given arguments
// TODO: reimplement this with the kurtosis SDK, it'll be cleaner.
func (d *KurtosisDeployer) runKurtosisCommand(argFile string) error {
data := templateData{
PackageName: d.packageName,
ArgFile: argFile,
Enclave: d.enclave,
}
var cmdBuf bytes.Buffer
if err := d.cmdTemplate.Execute(&cmdBuf, data); err != nil {
return fmt.Errorf("failed to execute command template: %w", err)
}
if d.dryRun {
fmt.Println("Dry run mode enabled, kurtosis would run the following command:")
fmt.Println(cmdBuf.String())
return nil
}
cmd := exec.Command("sh", "-c", cmdBuf.String())
cmd.Dir = d.baseDir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("kurtosis deployment failed: %w", err)
}
return nil
}
func (d *KurtosisDeployer) getWallets(wallets deployer.WalletList) WalletMap {
walletMap := make(WalletMap)
for _, wallet := range wallets {
walletMap[wallet.Name] = Wallet{
Address: wallet.Address,
PrivateKey: wallet.PrivateKey,
}
}
return walletMap
}
// getEnvironmentInfo parses the input spec and inspect output to create KurtosisEnvironment
func (d *KurtosisDeployer) getEnvironmentInfo(ctx context.Context, spec *spec.EnclaveSpec) (*KurtosisEnvironment, error) {
inspectResult, err := d.enclaveInspecter.EnclaveInspect(ctx, d.enclave)
if err != nil {
return nil, fmt.Errorf("failed to parse inspect output: %w", err)
}
// Get contract addresses
deployerState, err := d.enclaveObserver.EnclaveObserve(ctx, d.enclave)
if err != nil {
return nil, fmt.Errorf("failed to parse deployer state: %w", err)
}
env := &KurtosisEnvironment{
L2: make([]*Chain, 0, len(spec.Chains)),
Wallets: d.getWallets(deployerState.Wallets),
}
// Find L1 endpoint
if nodes, endpoints := findL1Endpoints(inspectResult.UserServices); len(nodes) > 0 {
env.L1 = &Chain{
Name: "Ethereum",
Services: endpoints,
Nodes: nodes,
}
}
// Find L2 endpoints
for _, chainSpec := range spec.Chains {
nodes, endpoints := findL2Endpoints(inspectResult.UserServices, fmt.Sprintf("-%s", chainSpec.Name))
chain := &Chain{
Name: chainSpec.Name,
ID: chainSpec.NetworkID,
Services: endpoints,
Nodes: nodes,
}
// Add contract addresses if available
if addresses, ok := deployerState.State[chainSpec.NetworkID]; ok {
chain.Addresses = addresses
}
env.L2 = append(env.L2, chain)
}
return env, nil
}
// Deploy executes the Kurtosis deployment command with the provided input
func (d *KurtosisDeployer) Deploy(ctx context.Context, input io.Reader) (*KurtosisEnvironment, error) {
// Parse the input spec first
inputCopy := new(bytes.Buffer)
tee := io.TeeReader(input, inputCopy)
spec, err := d.enclaveSpec.EnclaveSpec(tee)
if err != nil {
return nil, fmt.Errorf("failed to parse input spec: %w", err)
}
// Prepare argument file
argFile, err := d.prepareArgFile(inputCopy)
if err != nil {
return nil, err
}
defer os.Remove(argFile)
// Run kurtosis command
if err := d.runKurtosisCommand(argFile); err != nil {
return nil, err
}
// If dry run, return empty environment
if d.dryRun {
return &KurtosisEnvironment{}, nil
}
// Get environment information
return d.getEnvironmentInfo(ctx, spec)
}
package kurtosis
import (
"context"
"fmt"
"io"
"os"
"strings"
"testing"
"text/template"
"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/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFindRPCEndpoints(t *testing.T) {
testServices := inspect.ServiceMap{
"el-1-geth-lighthouse": {
"metrics": 52643,
"tcp-discovery": 52644,
"udp-discovery": 51936,
"engine-rpc": 52642,
"rpc": 52645,
"ws": 52646,
},
"op-batcher-op-kurtosis": {
"http": 53572,
},
"op-cl-1-op-node-op-geth-op-kurtosis": {
"udp-discovery": 50990,
"http": 53503,
"tcp-discovery": 53504,
},
"op-el-1-op-geth-op-node-op-kurtosis": {
"udp-discovery": 53233,
"engine-rpc": 53399,
"metrics": 53400,
"rpc": 53402,
"ws": 53403,
"tcp-discovery": 53401,
},
"vc-1-geth-lighthouse": {
"metrics": 53149,
},
"cl-1-lighthouse-geth": {
"metrics": 52691,
"tcp-discovery": 52692,
"udp-discovery": 58275,
"http": 52693,
},
}
tests := []struct {
name string
services inspect.ServiceMap
lookupFn func(inspect.ServiceMap) ([]Node, EndpointMap)
wantNodes []Node
wantEndpoints EndpointMap
}{
{
name: "find L1 endpoints",
services: testServices,
lookupFn: findL1Endpoints,
wantNodes: []Node{
{
"cl": "http://localhost:52693",
"el": "http://localhost:52645",
},
},
wantEndpoints: EndpointMap{},
},
{
name: "find op-kurtosis L2 endpoints",
services: testServices,
lookupFn: func(services inspect.ServiceMap) ([]Node, EndpointMap) {
return findL2Endpoints(services, "-op-kurtosis")
},
wantNodes: []Node{
{
"cl": "http://localhost:53503",
"el": "http://localhost:53402",
},
},
wantEndpoints: EndpointMap{
"batcher": "http://localhost:53572",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotNodes, gotEndpoints := tt.lookupFn(tt.services)
assert.Equal(t, tt.wantNodes, gotNodes)
assert.Equal(t, tt.wantEndpoints, gotEndpoints)
})
}
}
func TestKurtosisDeployer(t *testing.T) {
tests := []struct {
name string
opts []KurtosisDeployerOptions
wantBaseDir string
wantPkg string
wantDryRun bool
wantEnclave string
}{
{
name: "default values",
opts: nil,
wantBaseDir: ".",
wantPkg: DefaultPackageName,
wantDryRun: false,
wantEnclave: "devnet",
},
{
name: "with options",
opts: []KurtosisDeployerOptions{
WithKurtosisBaseDir("/custom/dir"),
WithKurtosisPackageName("custom-package"),
WithKurtosisDryRun(true),
WithKurtosisEnclave("custom-enclave"),
},
wantBaseDir: "/custom/dir",
wantPkg: "custom-package",
wantDryRun: true,
wantEnclave: "custom-enclave",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := NewKurtosisDeployer(tt.opts...)
assert.Equal(t, tt.wantBaseDir, d.baseDir)
assert.Equal(t, tt.wantPkg, d.packageName)
assert.Equal(t, tt.wantDryRun, d.dryRun)
assert.Equal(t, tt.wantEnclave, d.enclave)
})
}
}
func TestPrepareArgFile(t *testing.T) {
d := NewKurtosisDeployer()
input := strings.NewReader("test content")
path, err := d.prepareArgFile(input)
require.NoError(t, err)
defer func() {
err := os.Remove(path)
require.NoError(t, err)
}()
content, err := os.ReadFile(path)
require.NoError(t, err)
assert.Equal(t, "test content", string(content))
}
func TestRunKurtosisCommand(t *testing.T) {
fakeCmdTemplate := template.Must(template.New("fake_cmd").Parse("echo 'would run: {{.PackageName}} {{.ArgFile}} {{.Enclave}}'"))
tests := []struct {
name string
dryRun bool
wantError bool
wantOutput bool
cmdTemplate *template.Template
}{
{
name: "dry run",
dryRun: true,
wantError: false,
cmdTemplate: fakeCmdTemplate,
},
{
name: "successful run",
dryRun: false,
wantError: false,
wantOutput: true,
cmdTemplate: fakeCmdTemplate,
},
{
name: "template error",
dryRun: false,
wantError: true,
cmdTemplate: template.Must(template.New("bad_cmd").Parse("{{.NonExistentField}}")),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := NewKurtosisDeployer(
WithKurtosisDryRun(tt.dryRun),
WithKurtosisCmdTemplate(tt.cmdTemplate),
)
err := d.runKurtosisCommand("test.yaml")
if tt.wantError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
// fakeEnclaveInspecter implements EnclaveInspecter for testing
type fakeEnclaveInspecter struct {
result *inspect.InspectData
err error
}
func (f *fakeEnclaveInspecter) EnclaveInspect(ctx context.Context, enclave string) (*inspect.InspectData, error) {
return f.result, f.err
}
// fakeEnclaveObserver implements EnclaveObserver for testing
type fakeEnclaveObserver struct {
state *deployer.DeployerData
err error
}
func (f *fakeEnclaveObserver) EnclaveObserve(ctx context.Context, enclave string) (*deployer.DeployerData, error) {
return f.state, f.err
}
// fakeEnclaveSpecifier implements EnclaveSpecifier for testing
type fakeEnclaveSpecifier struct {
spec *spec.EnclaveSpec
err error
}
func (f *fakeEnclaveSpecifier) EnclaveSpec(r io.Reader) (*spec.EnclaveSpec, error) {
return f.spec, f.err
}
func TestDeploy(t *testing.T) {
// Create a template that just echoes the command that would be run
fakeCmdTemplate := template.Must(template.New("fake_cmd").Parse("echo 'would run: {{.PackageName}} {{.ArgFile}} {{.Enclave}}'"))
testSpecWithL2 := &spec.EnclaveSpec{
Chains: []spec.ChainSpec{
{
Name: "op-kurtosis",
NetworkID: "1234",
},
},
}
testSpecNoL2 := &spec.EnclaveSpec{
Chains: []spec.ChainSpec{},
}
testServices := inspect.ServiceMap{
"el-1-geth-lighthouse": {
"rpc": 52645,
},
"op-el-1-op-geth-op-node-op-kurtosis": {
"rpc": 53402,
},
"op-cl-1-op-node-op-geth-op-kurtosis": {
"http": 53503,
},
"op-batcher-op-kurtosis": {
"http": 53572,
},
}
testWallets := deployer.WalletList{
{
Name: "test-wallet",
Address: "0x123",
PrivateKey: "0xabc",
},
}
testAddresses := deployer.DeploymentAddresses{
"contract1": "0xdef",
}
tests := []struct {
name string
input string
spec *spec.EnclaveSpec
specErr error
inspectResult *inspect.InspectData
inspectErr error
deployerState *deployer.DeployerData
deployerErr error
dryRun bool
wantL1Nodes []Node
wantL2Nodes []Node
wantL2Services EndpointMap
wantWallets WalletMap
wantErr bool
}{
{
name: "successful deployment",
input: "test input",
spec: testSpecWithL2,
inspectResult: &inspect.InspectData{
UserServices: testServices,
},
deployerState: &deployer.DeployerData{
Wallets: testWallets,
State: map[string]deployer.DeploymentAddresses{
"1234": testAddresses,
},
},
wantL1Nodes: []Node{
{
"el": "http://localhost:52645",
},
},
wantL2Nodes: []Node{
{
"el": "http://localhost:53402",
"cl": "http://localhost:53503",
},
},
wantL2Services: EndpointMap{
"batcher": "http://localhost:53572",
},
wantWallets: WalletMap{
"test-wallet": {
Address: "0x123",
PrivateKey: "0xabc",
},
},
},
{
name: "spec error",
input: "test input",
spec: testSpecWithL2,
specErr: fmt.Errorf("spec failed"),
wantErr: true,
},
{
name: "dry run",
input: "test input",
spec: testSpecWithL2,
dryRun: true,
},
{
name: "inspect error",
input: "test input",
spec: testSpecWithL2,
inspectErr: fmt.Errorf("inspect failed"),
wantErr: true,
},
{
name: "deployer error",
input: "test input",
spec: testSpecWithL2,
inspectResult: &inspect.InspectData{
UserServices: testServices,
},
deployerErr: fmt.Errorf("deployer failed"),
wantErr: true,
},
{
name: "successful deployment with no L1",
input: "test input",
spec: testSpecWithL2,
inspectResult: &inspect.InspectData{
UserServices: inspect.ServiceMap{
"op-el-1-op-geth-op-node-op-kurtosis": {
"rpc": 53402,
},
"op-cl-1-op-node-op-geth-op-kurtosis": {
"http": 53503,
},
},
},
deployerState: &deployer.DeployerData{
Wallets: testWallets,
State: map[string]deployer.DeploymentAddresses{
"1234": testAddresses,
},
},
wantL2Nodes: []Node{
{
"el": "http://localhost:53402",
"cl": "http://localhost:53503",
},
},
wantWallets: WalletMap{
"test-wallet": {
Address: "0x123",
PrivateKey: "0xabc",
},
},
},
{
name: "successful deployment with no L2",
input: "test input",
spec: testSpecNoL2,
inspectResult: &inspect.InspectData{
UserServices: inspect.ServiceMap{
"el-1-geth-lighthouse": {
"rpc": 52645,
},
},
},
deployerState: &deployer.DeployerData{
Wallets: testWallets,
},
wantL1Nodes: []Node{
{
"el": "http://localhost:52645",
},
},
wantWallets: WalletMap{
"test-wallet": {
Address: "0x123",
PrivateKey: "0xabc",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := NewKurtosisDeployer(
WithKurtosisDryRun(tt.dryRun),
WithKurtosisCmdTemplate(fakeCmdTemplate),
WithKurtosisEnclaveSpec(&fakeEnclaveSpecifier{
spec: tt.spec,
err: tt.specErr,
}),
WithKurtosisEnclaveInspecter(&fakeEnclaveInspecter{
result: tt.inspectResult,
err: tt.inspectErr,
}),
WithKurtosisEnclaveObserver(&fakeEnclaveObserver{
state: tt.deployerState,
err: tt.deployerErr,
}),
)
env, err := d.Deploy(context.Background(), strings.NewReader(tt.input))
if tt.wantErr {
assert.Error(t, err)
return
}
require.NoError(t, err)
if tt.dryRun {
assert.NotNil(t, env)
assert.Empty(t, env.L1)
assert.Empty(t, env.L2)
assert.Empty(t, env.Wallets)
return
}
if tt.wantL1Nodes != nil {
assert.Equal(t, tt.wantL1Nodes, env.L1.Nodes)
} else {
assert.Nil(t, env.L1)
}
if len(tt.wantL2Nodes) > 0 {
assert.Equal(t, tt.wantL2Nodes, env.L2[0].Nodes)
if tt.wantL2Services != nil {
assert.Equal(t, tt.wantL2Services, env.L2[0].Services)
}
if addresses, ok := tt.deployerState.State["1234"]; ok {
assert.Equal(t, addresses, env.L2[0].Addresses)
}
} else {
assert.Empty(t, env.L2)
}
assert.Equal(t, tt.wantWallets, env.Wallets)
})
}
}
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