Commit cb755961 authored by Yann Hodique's avatar Yann Hodique Committed by GitHub

feat(kurtosis-devnet): split SDK-based logic into separate pkgs (#13581)

This is a code cleanup change.

This moves all the code built on top of Kurtosis SDK into separate
packages for better clarity.
parent 095dbf56
...@@ -245,12 +245,15 @@ func deploy(ctx context.Context, cfg *config, r io.Reader) error { ...@@ -245,12 +245,15 @@ func deploy(ctx context.Context, cfg *config, r io.Reader) error {
return fmt.Errorf("error copying deployment input: %w", err) return fmt.Errorf("error copying deployment input: %w", err)
} }
kurtosisDeployer := kurtosis.NewKurtosisDeployer( kurtosisDeployer, err := kurtosis.NewKurtosisDeployer(
kurtosis.WithKurtosisBaseDir(cfg.baseDir), kurtosis.WithKurtosisBaseDir(cfg.baseDir),
kurtosis.WithKurtosisDryRun(cfg.dryRun), kurtosis.WithKurtosisDryRun(cfg.dryRun),
kurtosis.WithKurtosisPackageName(cfg.kurtosisPackage), kurtosis.WithKurtosisPackageName(cfg.kurtosisPackage),
kurtosis.WithKurtosisEnclave(cfg.enclave), kurtosis.WithKurtosisEnclave(cfg.enclave),
) )
if err != nil {
return fmt.Errorf("error creating kurtosis deployer: %w", err)
}
env, err := kurtosisDeployer.Deploy(ctx, buf) env, err := kurtosisDeployer.Deploy(ctx, buf)
if err != nil { if err != nil {
......
package fake
import (
"context"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/interfaces"
"github.com/kurtosis-tech/kurtosis/api/golang/core/lib/starlark_run_config"
)
// KurtosisContext implements interfaces.KurtosisContextInterface for testing
type KurtosisContext struct {
EnclaveCtx *EnclaveContext
GetErr error
CreateErr error
}
func (f *KurtosisContext) CreateEnclave(ctx context.Context, name string) (interfaces.EnclaveContext, error) {
if f.CreateErr != nil {
return nil, f.CreateErr
}
return f.EnclaveCtx, nil
}
func (f *KurtosisContext) GetEnclave(ctx context.Context, name string) (interfaces.EnclaveContext, error) {
if f.GetErr != nil {
return nil, f.GetErr
}
return f.EnclaveCtx, nil
}
// EnclaveContext implements interfaces.EnclaveContext for testing
type EnclaveContext struct {
RunErr error
Responses []interfaces.StarlarkResponse
}
func (f *EnclaveContext) RunStarlarkPackage(ctx context.Context, pkg string, params *starlark_run_config.StarlarkRunConfig) (<-chan interfaces.StarlarkResponse, string, error) {
if f.RunErr != nil {
return nil, "", f.RunErr
}
// Create a channel and send all responses
ch := make(chan interfaces.StarlarkResponse)
go func() {
defer close(ch)
for _, resp := range f.Responses {
ch <- resp
}
}()
return ch, "", nil
}
// StarlarkResponse implements interfaces.StarlarkResponse for testing
type StarlarkResponse struct {
Err interfaces.StarlarkError
ProgressMsg []string
Instruction string
IsSuccessful bool
Warning string
Info string
Result string
HasResult bool // tracks whether result was explicitly set
}
func (f *StarlarkResponse) GetError() interfaces.StarlarkError {
return f.Err
}
func (f *StarlarkResponse) GetProgressInfo() interfaces.ProgressInfo {
if f.ProgressMsg != nil {
return &ProgressInfo{Info: f.ProgressMsg}
}
return nil
}
func (f *StarlarkResponse) GetInstruction() interfaces.Instruction {
if f.Instruction != "" {
return &Instruction{Desc: f.Instruction}
}
return nil
}
func (f *StarlarkResponse) GetRunFinishedEvent() interfaces.RunFinishedEvent {
return &RunFinishedEvent{IsSuccessful: f.IsSuccessful}
}
func (f *StarlarkResponse) GetWarning() interfaces.Warning {
if f.Warning != "" {
return &Warning{Msg: f.Warning}
}
return nil
}
func (f *StarlarkResponse) GetInfo() interfaces.Info {
if f.Info != "" {
return &Info{Msg: f.Info}
}
return nil
}
func (f *StarlarkResponse) GetInstructionResult() interfaces.InstructionResult {
if !f.HasResult {
return nil
}
return &InstructionResult{Result: f.Result}
}
// ProgressInfo implements ProgressInfo for testing
type ProgressInfo struct {
Info []string
}
func (f *ProgressInfo) GetCurrentStepInfo() []string {
return f.Info
}
// Instruction implements Instruction for testing
type Instruction struct {
Desc string
}
func (f *Instruction) GetDescription() string {
return f.Desc
}
// StarlarkError implements StarlarkError for testing
type StarlarkError struct {
InterpretationErr error
ValidationErr error
ExecutionErr error
}
func (f *StarlarkError) GetInterpretationError() error {
return f.InterpretationErr
}
func (f *StarlarkError) GetValidationError() error {
return f.ValidationErr
}
func (f *StarlarkError) GetExecutionError() error {
return f.ExecutionErr
}
// RunFinishedEvent implements RunFinishedEvent for testing
type RunFinishedEvent struct {
IsSuccessful bool
}
func (f *RunFinishedEvent) GetIsRunSuccessful() bool {
return f.IsSuccessful
}
// Warning implements Warning for testing
type Warning struct {
Msg string
}
func (f *Warning) GetMessage() string {
return f.Msg
}
// Info implements Info for testing
type Info struct {
Msg string
}
func (f *Info) GetMessage() string {
return f.Msg
}
// InstructionResult implements InstructionResult for testing
type InstructionResult struct {
Result string
}
func (f *InstructionResult) GetSerializedInstructionResult() string {
return f.Result
}
package interfaces
import (
"context"
"github.com/kurtosis-tech/kurtosis/api/golang/core/lib/starlark_run_config"
)
// Interfaces for Kurtosis SDK types to make testing easier
type StarlarkError interface {
GetInterpretationError() error
GetValidationError() error
GetExecutionError() error
}
type ProgressInfo interface {
GetCurrentStepInfo() []string
}
type Instruction interface {
GetDescription() string
}
type RunFinishedEvent interface {
GetIsRunSuccessful() bool
}
type Warning interface {
GetMessage() string
}
type Info interface {
GetMessage() string
}
type InstructionResult interface {
GetSerializedInstructionResult() string
}
type StarlarkResponse interface {
GetError() StarlarkError
GetProgressInfo() ProgressInfo
GetInstruction() Instruction
GetRunFinishedEvent() RunFinishedEvent
GetWarning() Warning
GetInfo() Info
GetInstructionResult() InstructionResult
}
type EnclaveContext interface {
RunStarlarkPackage(context.Context, string, *starlark_run_config.StarlarkRunConfig) (<-chan StarlarkResponse, string, error)
}
type KurtosisContextInterface interface {
CreateEnclave(context.Context, string) (EnclaveContext, error)
GetEnclave(context.Context, string) (EnclaveContext, error)
}
package run
import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/interfaces"
"github.com/fatih/color"
)
// Color printers
var (
printCyan = color.New(color.FgCyan).SprintFunc()
printYellow = color.New(color.FgYellow).SprintFunc()
printRed = color.New(color.FgRed).SprintFunc()
printBlue = color.New(color.FgBlue).SprintFunc()
)
// MessageHandler defines the interface for handling different types of messages
type MessageHandler interface {
// Handle processes the message if applicable and returns:
// - bool: whether the message was handled
// - error: any error that occurred during handling
Handle(context.Context, interfaces.StarlarkResponse) (bool, error)
}
// MessageHandlerFunc is a function type that implements MessageHandler
type MessageHandlerFunc func(context.Context, interfaces.StarlarkResponse) (bool, error)
func (f MessageHandlerFunc) Handle(ctx context.Context, resp interfaces.StarlarkResponse) (bool, error) {
return f(ctx, resp)
}
// FirstMatchHandler returns a handler that applies the first matching handler from the given handlers
func FirstMatchHandler(handlers ...MessageHandler) MessageHandler {
return MessageHandlerFunc(func(ctx context.Context, resp interfaces.StarlarkResponse) (bool, error) {
for _, h := range handlers {
handled, err := h.Handle(ctx, resp)
if err != nil {
return true, err
}
if handled {
return true, nil
}
}
return false, nil
})
}
// AllHandlers returns a handler that applies all the given handlers in order
func AllHandlers(handlers ...MessageHandler) MessageHandler {
return MessageHandlerFunc(func(ctx context.Context, resp interfaces.StarlarkResponse) (bool, error) {
anyHandled := false
for _, h := range handlers {
handled, err := h.Handle(ctx, resp)
if err != nil {
return true, err
}
anyHandled = anyHandled || handled
}
return anyHandled, nil
})
}
// defaultHandler is the default message handler that provides standard Kurtosis output
var defaultHandler = FirstMatchHandler(
MessageHandlerFunc(handleProgress),
MessageHandlerFunc(handleInstruction),
MessageHandlerFunc(handleWarning),
MessageHandlerFunc(handleInfo),
MessageHandlerFunc(handleResult),
MessageHandlerFunc(handleError),
)
// handleProgress handles progress info messages
func handleProgress(ctx context.Context, resp interfaces.StarlarkResponse) (bool, error) {
if progressInfo := resp.GetProgressInfo(); progressInfo != nil {
// ignore progress messages, same as kurtosis run does
return true, nil
}
return false, nil
}
// handleInstruction handles instruction messages
func handleInstruction(ctx context.Context, resp interfaces.StarlarkResponse) (bool, error) {
if instruction := resp.GetInstruction(); instruction != nil {
desc := instruction.GetDescription()
fmt.Println(printCyan(desc))
return true, nil
}
return false, nil
}
// handleWarning handles warning messages
func handleWarning(ctx context.Context, resp interfaces.StarlarkResponse) (bool, error) {
if warning := resp.GetWarning(); warning != nil {
fmt.Println(printYellow(warning.GetMessage()))
return true, nil
}
return false, nil
}
// handleInfo handles info messages
func handleInfo(ctx context.Context, resp interfaces.StarlarkResponse) (bool, error) {
if info := resp.GetInfo(); info != nil {
fmt.Println(printBlue(info.GetMessage()))
return true, nil
}
return false, nil
}
// handleResult handles instruction result messages
func handleResult(ctx context.Context, resp interfaces.StarlarkResponse) (bool, error) {
if result := resp.GetInstructionResult(); result != nil {
if result.GetSerializedInstructionResult() != "" {
fmt.Printf("%s\n\n", result.GetSerializedInstructionResult())
}
return true, nil
}
return false, nil
}
// handleError handles error messages
func handleError(ctx context.Context, resp interfaces.StarlarkResponse) (bool, error) {
if err := resp.GetError(); err != nil {
if interpretErr := err.GetInterpretationError(); interpretErr != nil {
return true, fmt.Errorf(printRed("interpretation error: %v"), interpretErr)
}
if validationErr := err.GetValidationError(); validationErr != nil {
return true, fmt.Errorf(printRed("validation error: %v"), validationErr)
}
if executionErr := err.GetExecutionError(); executionErr != nil {
return true, fmt.Errorf(printRed("execution error: %v"), executionErr)
}
return true, nil
}
return false, nil
}
// makeRunFinishedHandler creates a handler for run finished events
func makeRunFinishedHandler(isSuccessful *bool) MessageHandlerFunc {
return func(ctx context.Context, resp interfaces.StarlarkResponse) (bool, error) {
if event := resp.GetRunFinishedEvent(); event != nil {
*isSuccessful = event.GetIsRunSuccessful()
return true, nil
}
return false, nil
}
}
package run
import (
"context"
"errors"
"fmt"
"io"
"os"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/interfaces"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/wrappers"
"github.com/kurtosis-tech/kurtosis/api/golang/core/lib/starlark_run_config"
)
type KurtosisRunner struct {
dryRun bool
enclave string
kurtosisCtx interfaces.KurtosisContextInterface
runHandlers []MessageHandler
}
type KurtosisRunnerOptions func(*KurtosisRunner)
func WithKurtosisRunnerDryRun(dryRun bool) KurtosisRunnerOptions {
return func(r *KurtosisRunner) {
r.dryRun = dryRun
}
}
func WithKurtosisRunnerEnclave(enclave string) KurtosisRunnerOptions {
return func(r *KurtosisRunner) {
r.enclave = enclave
}
}
func WithKurtosisRunnerKurtosisContext(kurtosisCtx interfaces.KurtosisContextInterface) KurtosisRunnerOptions {
return func(r *KurtosisRunner) {
r.kurtosisCtx = kurtosisCtx
}
}
func WithKurtosisRunnerRunHandlers(runHandlers ...MessageHandler) KurtosisRunnerOptions {
return func(r *KurtosisRunner) {
r.runHandlers = runHandlers
}
}
func NewKurtosisRunner(opts ...KurtosisRunnerOptions) (*KurtosisRunner, error) {
r := &KurtosisRunner{}
for _, opt := range opts {
opt(r)
}
if r.kurtosisCtx == nil {
var err error
r.kurtosisCtx, err = wrappers.GetDefaultKurtosisContext()
if err != nil {
return nil, fmt.Errorf("failed to create Kurtosis context: %w", err)
}
}
return r, nil
}
func (r *KurtosisRunner) Run(ctx context.Context, packageName string, args io.Reader) error {
if r.dryRun {
fmt.Printf("Dry run mode enabled, would run kurtosis package %s in enclave %s\n",
packageName, r.enclave)
if args != nil {
fmt.Println("\nWith arguments:")
if _, err := io.Copy(os.Stdout, args); err != nil {
return fmt.Errorf("failed to dump args: %w", err)
}
fmt.Println()
}
return nil
}
// Try to get existing enclave first
enclaveCtx, err := r.kurtosisCtx.GetEnclave(ctx, r.enclave)
if err != nil {
// If enclave doesn't exist, create a new one
fmt.Printf("Creating a new enclave for Starlark to run inside...\n")
enclaveCtx, err = r.kurtosisCtx.CreateEnclave(ctx, r.enclave)
if err != nil {
return fmt.Errorf("failed to create enclave: %w", err)
}
fmt.Printf("Enclave '%s' created successfully\n\n", r.enclave)
} else {
fmt.Printf("Using existing enclave '%s'\n\n", r.enclave)
}
// Set up run config with args if provided
var serializedParams string
if args != nil {
argsBytes, err := io.ReadAll(args)
if err != nil {
return fmt.Errorf("failed to read args: %w", err)
}
serializedParams = string(argsBytes)
}
runConfig := &starlark_run_config.StarlarkRunConfig{
SerializedParams: serializedParams,
}
stream, _, err := enclaveCtx.RunStarlarkPackage(ctx, packageName, runConfig)
if err != nil {
return fmt.Errorf("failed to run Kurtosis package: %w", err)
}
// Set up message handlers
var isRunSuccessful bool
runFinishedHandler := makeRunFinishedHandler(&isRunSuccessful)
// Combine custom handlers with default handler and run finished handler
handler := AllHandlers(append(r.runHandlers, defaultHandler, runFinishedHandler)...)
// Process the output stream
for responseLine := range stream {
if _, err := handler.Handle(ctx, responseLine); err != nil {
return err
}
}
if !isRunSuccessful {
return errors.New(printRed("kurtosis package execution failed"))
}
return nil
}
package run
import (
"context"
"fmt"
"testing"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/fake"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/interfaces"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRunKurtosis(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
testErr := fmt.Errorf("test error")
tests := []struct {
name string
responses []fake.StarlarkResponse
kurtosisErr error
getErr error
wantErr bool
}{
{
name: "successful run with all message types",
responses: []fake.StarlarkResponse{
{ProgressMsg: []string{"Starting deployment..."}},
{Info: "Preparing environment"},
{Instruction: "Executing package"},
{Warning: "Using default config"},
{Result: "Service started", HasResult: true},
{ProgressMsg: []string{"Deployment complete"}},
{IsSuccessful: true},
},
wantErr: false,
},
{
name: "run with error",
responses: []fake.StarlarkResponse{
{ProgressMsg: []string{"Starting deployment..."}},
{Err: &fake.StarlarkError{ExecutionErr: testErr}},
},
wantErr: true,
},
{
name: "run with unsuccessful completion",
responses: []fake.StarlarkResponse{
{ProgressMsg: []string{"Starting deployment..."}},
{IsSuccessful: false},
},
wantErr: true,
},
{
name: "kurtosis error",
kurtosisErr: fmt.Errorf("kurtosis failed"),
wantErr: true,
},
{
name: "uses existing enclave",
responses: []fake.StarlarkResponse{
{ProgressMsg: []string{"Using existing enclave"}},
{IsSuccessful: true},
},
getErr: nil,
wantErr: false,
},
{
name: "creates new enclave when get fails",
responses: []fake.StarlarkResponse{
{ProgressMsg: []string{"Creating new enclave"}},
{IsSuccessful: true},
},
getErr: fmt.Errorf("enclave not found"),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Convert test responses to interface slice
interfaceResponses := make([]interfaces.StarlarkResponse, len(tt.responses))
for i := range tt.responses {
interfaceResponses[i] = &tt.responses[i]
}
// Create a fake enclave context that will return our test responses
fakeCtx := &fake.KurtosisContext{
EnclaveCtx: &fake.EnclaveContext{
RunErr: tt.kurtosisErr,
Responses: interfaceResponses,
},
GetErr: tt.getErr,
}
kurtosisRunner, err := NewKurtosisRunner(
WithKurtosisRunnerDryRun(false),
WithKurtosisRunnerEnclave("test-enclave"),
WithKurtosisRunnerKurtosisContext(fakeCtx),
)
require.NoError(t, err)
err = kurtosisRunner.Run(ctx, "test-package", nil)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
package wrappers
import (
"context"
"fmt"
"strings"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/interfaces"
"github.com/kurtosis-tech/kurtosis/api/golang/core/kurtosis_core_rpc_api_bindings"
"github.com/kurtosis-tech/kurtosis/api/golang/core/lib/enclaves"
"github.com/kurtosis-tech/kurtosis/api/golang/core/lib/starlark_run_config"
"github.com/kurtosis-tech/kurtosis/api/golang/engine/lib/kurtosis_context"
)
// Wrapper types to implement our interfaces
type KurtosisContextWrapper struct {
*kurtosis_context.KurtosisContext
}
type EnclaveContextWrapper struct {
*enclaves.EnclaveContext
}
type starlarkRunResponseLineWrapper struct {
*kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine
}
type starlarkErrorWrapper struct {
*kurtosis_core_rpc_api_bindings.StarlarkError
}
type starlarkRunProgressWrapper struct {
*kurtosis_core_rpc_api_bindings.StarlarkRunProgress
}
type starlarkInstructionWrapper struct {
*kurtosis_core_rpc_api_bindings.StarlarkInstruction
}
type starlarkRunFinishedEventWrapper struct {
*kurtosis_core_rpc_api_bindings.StarlarkRunFinishedEvent
}
type starlarkWarningWrapper struct {
*kurtosis_core_rpc_api_bindings.StarlarkWarning
}
type starlarkInfoWrapper struct {
*kurtosis_core_rpc_api_bindings.StarlarkInfo
}
type starlarkInstructionResultWrapper struct {
*kurtosis_core_rpc_api_bindings.StarlarkInstructionResult
}
func (w KurtosisContextWrapper) CreateEnclave(ctx context.Context, name string) (interfaces.EnclaveContext, error) {
enclaveCtx, err := w.KurtosisContext.CreateEnclave(ctx, name)
if err != nil {
return nil, err
}
return &EnclaveContextWrapper{enclaveCtx}, nil
}
func (w KurtosisContextWrapper) GetEnclave(ctx context.Context, name string) (interfaces.EnclaveContext, error) {
enclaveCtx, err := w.KurtosisContext.GetEnclaveContext(ctx, name)
if err != nil {
return nil, err
}
return &EnclaveContextWrapper{enclaveCtx}, nil
}
func (w *EnclaveContextWrapper) RunStarlarkPackage(ctx context.Context, pkg string, serializedParams *starlark_run_config.StarlarkRunConfig) (<-chan interfaces.StarlarkResponse, string, error) {
runner := w.EnclaveContext.RunStarlarkPackage
if strings.HasPrefix(pkg, "github.com/") {
runner = w.EnclaveContext.RunStarlarkRemotePackage
}
stream, cancel, err := runner(ctx, pkg, serializedParams)
if err != nil {
return nil, "", err
}
// Convert the stream
wrappedStream := make(chan interfaces.StarlarkResponse)
go func() {
defer close(wrappedStream)
defer cancel()
for line := range stream {
wrappedStream <- &starlarkRunResponseLineWrapper{line}
}
}()
return wrappedStream, "", nil
}
func (w *starlarkRunResponseLineWrapper) GetError() interfaces.StarlarkError {
if err := w.StarlarkRunResponseLine.GetError(); err != nil {
return &starlarkErrorWrapper{err}
}
return nil
}
func (w *starlarkRunResponseLineWrapper) GetProgressInfo() interfaces.ProgressInfo {
if progress := w.StarlarkRunResponseLine.GetProgressInfo(); progress != nil {
return &starlarkRunProgressWrapper{progress}
}
return nil
}
func (w *starlarkRunResponseLineWrapper) GetInstruction() interfaces.Instruction {
if instruction := w.StarlarkRunResponseLine.GetInstruction(); instruction != nil {
return &starlarkInstructionWrapper{instruction}
}
return nil
}
func (w *starlarkRunResponseLineWrapper) GetRunFinishedEvent() interfaces.RunFinishedEvent {
if event := w.StarlarkRunResponseLine.GetRunFinishedEvent(); event != nil {
return &starlarkRunFinishedEventWrapper{event}
}
return nil
}
func (w *starlarkRunResponseLineWrapper) GetWarning() interfaces.Warning {
if warning := w.StarlarkRunResponseLine.GetWarning(); warning != nil {
return &starlarkWarningWrapper{warning}
}
return nil
}
func (w *starlarkRunResponseLineWrapper) GetInfo() interfaces.Info {
if info := w.StarlarkRunResponseLine.GetInfo(); info != nil {
return &starlarkInfoWrapper{info}
}
return nil
}
func (w *starlarkRunResponseLineWrapper) GetInstructionResult() interfaces.InstructionResult {
if result := w.StarlarkRunResponseLine.GetInstructionResult(); result != nil {
return &starlarkInstructionResultWrapper{result}
}
return nil
}
func (w *starlarkRunProgressWrapper) GetCurrentStepInfo() []string {
return w.StarlarkRunProgress.CurrentStepInfo
}
func (w *starlarkInstructionWrapper) GetDescription() string {
return w.StarlarkInstruction.Description
}
func (w *starlarkRunFinishedEventWrapper) GetIsRunSuccessful() bool {
return w.StarlarkRunFinishedEvent.IsRunSuccessful
}
func (w *starlarkErrorWrapper) GetInterpretationError() error {
if err := w.StarlarkError.GetInterpretationError(); err != nil {
return fmt.Errorf("%v", err)
}
return nil
}
func (w *starlarkErrorWrapper) GetValidationError() error {
if err := w.StarlarkError.GetValidationError(); err != nil {
return fmt.Errorf("%v", err)
}
return nil
}
func (w *starlarkErrorWrapper) GetExecutionError() error {
if err := w.StarlarkError.GetExecutionError(); err != nil {
return fmt.Errorf("%v", err)
}
return nil
}
func (w *starlarkWarningWrapper) GetMessage() string {
return w.StarlarkWarning.WarningMessage
}
func (w *starlarkInfoWrapper) GetMessage() string {
return w.StarlarkInfo.InfoMessage
}
func (w *starlarkInstructionResultWrapper) GetSerializedInstructionResult() string {
return w.StarlarkInstructionResult.SerializedInstructionResult
}
func GetDefaultKurtosisContext() (interfaces.KurtosisContextInterface, error) {
kCtx, err := kurtosis_context.NewKurtosisContextFromLocalEngine()
if err != nil {
return nil, fmt.Errorf("failed to create Kurtosis context: %w", err)
}
return KurtosisContextWrapper{
KurtosisContext: kCtx,
}, nil
}
...@@ -6,6 +6,9 @@ import ( ...@@ -6,6 +6,9 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/interfaces"
"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/sources/deployer" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/deployer"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec"
) )
...@@ -55,8 +58,7 @@ type KurtosisDeployer struct { ...@@ -55,8 +58,7 @@ type KurtosisDeployer struct {
enclaveSpec EnclaveSpecifier enclaveSpec EnclaveSpecifier
enclaveInspecter EnclaveInspecter enclaveInspecter EnclaveInspecter
enclaveObserver EnclaveObserver enclaveObserver EnclaveObserver
kurtosisCtx kurtosisContextInterface kurtosisCtx interfaces.KurtosisContextInterface
runHandlers []MessageHandler
} }
type KurtosisDeployerOptions func(*KurtosisDeployer) type KurtosisDeployerOptions func(*KurtosisDeployer)
...@@ -103,14 +105,14 @@ func WithKurtosisEnclaveObserver(enclaveObserver EnclaveObserver) KurtosisDeploy ...@@ -103,14 +105,14 @@ func WithKurtosisEnclaveObserver(enclaveObserver EnclaveObserver) KurtosisDeploy
} }
} }
func WithKurtosisRunHandlers(runHandlers []MessageHandler) KurtosisDeployerOptions { func WithKurtosisKurtosisContext(kurtosisCtx interfaces.KurtosisContextInterface) KurtosisDeployerOptions {
return func(d *KurtosisDeployer) { return func(d *KurtosisDeployer) {
d.runHandlers = runHandlers d.kurtosisCtx = kurtosisCtx
} }
} }
// NewKurtosisDeployer creates a new KurtosisDeployer instance // NewKurtosisDeployer creates a new KurtosisDeployer instance
func NewKurtosisDeployer(opts ...KurtosisDeployerOptions) *KurtosisDeployer { func NewKurtosisDeployer(opts ...KurtosisDeployerOptions) (*KurtosisDeployer, error) {
d := &KurtosisDeployer{ d := &KurtosisDeployer{
baseDir: ".", baseDir: ".",
packageName: DefaultPackageName, packageName: DefaultPackageName,
...@@ -126,7 +128,15 @@ func NewKurtosisDeployer(opts ...KurtosisDeployerOptions) *KurtosisDeployer { ...@@ -126,7 +128,15 @@ func NewKurtosisDeployer(opts ...KurtosisDeployerOptions) *KurtosisDeployer {
opt(d) opt(d)
} }
return d if d.kurtosisCtx == nil {
var err error
d.kurtosisCtx, err = wrappers.GetDefaultKurtosisContext()
if err != nil {
return nil, fmt.Errorf("failed to create Kurtosis context: %w", err)
}
}
return d, nil
} }
func (d *KurtosisDeployer) getWallets(wallets deployer.WalletList) WalletMap { func (d *KurtosisDeployer) getWallets(wallets deployer.WalletList) WalletMap {
...@@ -202,7 +212,16 @@ func (d *KurtosisDeployer) Deploy(ctx context.Context, input io.Reader) (*Kurtos ...@@ -202,7 +212,16 @@ func (d *KurtosisDeployer) Deploy(ctx context.Context, input io.Reader) (*Kurtos
} }
// Run kurtosis command // Run kurtosis command
if err := d.runKurtosis(ctx, inputCopy); err != nil { kurtosisRunner, err := run.NewKurtosisRunner(
run.WithKurtosisRunnerDryRun(d.dryRun),
run.WithKurtosisRunnerEnclave(d.enclave),
run.WithKurtosisRunnerKurtosisContext(d.kurtosisCtx),
)
if err != nil {
return nil, fmt.Errorf("failed to create Kurtosis runner: %w", err)
}
if err := kurtosisRunner.Run(ctx, d.packageName, inputCopy); err != nil {
return nil, err return nil, err
} }
......
This diff is collapsed.
...@@ -7,6 +7,8 @@ import ( ...@@ -7,6 +7,8 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/fake"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/api/interfaces"
"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/inspect"
"github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec" "github.com/ethereum-optimism/optimism/kurtosis-devnet/pkg/kurtosis/sources/spec"
...@@ -48,7 +50,8 @@ func TestKurtosisDeployer(t *testing.T) { ...@@ -48,7 +50,8 @@ func TestKurtosisDeployer(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) {
d := NewKurtosisDeployer(tt.opts...) d, err := NewKurtosisDeployer(tt.opts...)
require.NoError(t, err)
assert.Equal(t, tt.wantBaseDir, d.baseDir) assert.Equal(t, tt.wantBaseDir, d.baseDir)
assert.Equal(t, tt.wantPkg, d.packageName) assert.Equal(t, tt.wantPkg, d.packageName)
assert.Equal(t, tt.wantDryRun, d.dryRun) assert.Equal(t, tt.wantDryRun, d.dryRun)
...@@ -88,7 +91,7 @@ func (f *fakeEnclaveSpecifier) EnclaveSpec(r io.Reader) (*spec.EnclaveSpec, erro ...@@ -88,7 +91,7 @@ func (f *fakeEnclaveSpecifier) EnclaveSpec(r io.Reader) (*spec.EnclaveSpec, erro
} }
func TestDeploy(t *testing.T) { func TestDeploy(t *testing.T) {
testSpecWithL2 := &spec.EnclaveSpec{ testSpec := &spec.EnclaveSpec{
Chains: []spec.ChainSpec{ Chains: []spec.ChainSpec{
{ {
Name: "op-kurtosis", Name: "op-kurtosis",
...@@ -97,32 +100,10 @@ func TestDeploy(t *testing.T) { ...@@ -97,32 +100,10 @@ func TestDeploy(t *testing.T) {
}, },
} }
testSpecNoL2 := &spec.EnclaveSpec{
Chains: []spec.ChainSpec{},
}
// Define successful responses that will be used in multiple test cases
successResponses := []fakeStarlarkResponse{
{progressMsg: []string{"Starting deployment..."}},
{info: "Preparing environment"},
{instruction: "Executing package"},
{progressMsg: []string{"Deployment complete"}},
{isSuccessful: true},
}
testServices := make(inspect.ServiceMap) testServices := make(inspect.ServiceMap)
testServices["el-1-geth-lighthouse"] = inspect.PortMap{ testServices["el-1-geth-lighthouse"] = inspect.PortMap{
"rpc": {Port: 52645}, "rpc": {Port: 52645},
} }
testServices["op-el-1-op-geth-op-node-op-kurtosis"] = inspect.PortMap{
"rpc": {Port: 53402},
}
testServices["op-cl-1-op-node-op-geth-op-kurtosis"] = inspect.PortMap{
"http": {Port: 53503},
}
testServices["op-batcher-op-kurtosis"] = inspect.PortMap{
"http": {Port: 53572},
}
testWallets := deployer.WalletList{ testWallets := deployer.WalletList{
{ {
...@@ -132,211 +113,82 @@ func TestDeploy(t *testing.T) { ...@@ -132,211 +113,82 @@ func TestDeploy(t *testing.T) {
}, },
} }
testAddresses := deployer.DeploymentAddresses{
"contract1": "0xdef",
}
tests := []struct { tests := []struct {
name string name string
input string
spec *spec.EnclaveSpec
specErr error specErr error
inspectResult *inspect.InspectData
inspectErr error inspectErr error
deployerState *deployer.DeployerData
deployerErr error deployerErr error
kurtosisErr error kurtosisErr error
responses []fakeStarlarkResponse
wantL1Nodes []Node
wantL2Nodes []Node
wantL2Services EndpointMap
wantWallets WalletMap
wantErr bool wantErr bool
}{ }{
{ {
name: "successful deployment", 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,
},
},
responses: successResponses,
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", name: "spec error",
input: "test input",
spec: testSpecWithL2,
specErr: fmt.Errorf("spec failed"), specErr: fmt.Errorf("spec failed"),
wantErr: true, wantErr: true,
}, },
{ {
name: "inspect error", name: "inspect error",
input: "test input",
spec: testSpecWithL2,
inspectErr: fmt.Errorf("inspect failed"), inspectErr: fmt.Errorf("inspect failed"),
wantErr: true, wantErr: true,
}, },
{ {
name: "kurtosis error", name: "kurtosis error",
input: "test input",
spec: testSpecWithL2,
kurtosisErr: fmt.Errorf("kurtosis failed"), kurtosisErr: fmt.Errorf("kurtosis failed"),
wantErr: true, wantErr: true,
}, },
{ {
name: "deployer error", name: "deployer error",
input: "test input",
spec: testSpecWithL2,
inspectResult: &inspect.InspectData{
UserServices: testServices,
},
deployerErr: fmt.Errorf("deployer failed"), deployerErr: fmt.Errorf("deployer failed"),
wantErr: true, 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": inspect.PortMap{
"rpc": {Port: 53402},
},
"op-cl-1-op-node-op-geth-op-kurtosis": inspect.PortMap{
"http": {Port: 53503},
},
},
},
deployerState: &deployer.DeployerData{
Wallets: testWallets,
State: map[string]deployer.DeploymentAddresses{
"1234": testAddresses,
},
},
responses: successResponses,
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": inspect.PortMap{
"rpc": {Port: 52645},
},
},
},
deployerState: &deployer.DeployerData{
Wallets: testWallets,
},
responses: successResponses,
wantL1Nodes: []Node{
{
"el": "http://localhost:52645",
},
},
wantWallets: WalletMap{
"test-wallet": {
Address: "0x123",
PrivateKey: "0xabc",
},
},
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
// Create a fake Kurtosis context // Create a fake Kurtosis context that will return the test error
fakeCtx := &fakeKurtosisContext{ fakeCtx := &fake.KurtosisContext{
enclaveCtx: &fakeEnclaveContext{ EnclaveCtx: &fake.EnclaveContext{
runErr: tt.kurtosisErr, RunErr: tt.kurtosisErr,
responses: tt.responses, // Send a successful run finished event for successful cases
Responses: []interfaces.StarlarkResponse{
&fake.StarlarkResponse{
IsSuccessful: !tt.wantErr,
},
},
}, },
} }
d := NewKurtosisDeployer( d, err := NewKurtosisDeployer(
WithKurtosisEnclaveSpec(&fakeEnclaveSpecifier{ WithKurtosisEnclaveSpec(&fakeEnclaveSpecifier{
spec: tt.spec, spec: testSpec,
err: tt.specErr, err: tt.specErr,
}), }),
WithKurtosisEnclaveInspecter(&fakeEnclaveInspecter{ WithKurtosisEnclaveInspecter(&fakeEnclaveInspecter{
result: tt.inspectResult, result: &inspect.InspectData{
UserServices: testServices,
},
err: tt.inspectErr, err: tt.inspectErr,
}), }),
WithKurtosisEnclaveObserver(&fakeEnclaveObserver{ WithKurtosisEnclaveObserver(&fakeEnclaveObserver{
state: tt.deployerState, state: &deployer.DeployerData{
Wallets: testWallets,
},
err: tt.deployerErr, err: tt.deployerErr,
}), }),
WithKurtosisKurtosisContext(fakeCtx),
) )
require.NoError(t, err)
// Set the fake Kurtosis context _, err = d.Deploy(context.Background(), strings.NewReader("test input"))
d.kurtosisCtx = fakeCtx
env, err := d.Deploy(context.Background(), strings.NewReader(tt.input))
if tt.wantErr { if tt.wantErr {
assert.Error(t, err) assert.Error(t, err)
return return
} }
require.NoError(t, err) require.NoError(t, err)
assert.NotNil(t, env)
if tt.wantL1Nodes != nil {
require.NotNil(t, env.L1)
assert.Equal(t, tt.wantL1Nodes, env.L1.Nodes)
} else {
assert.Nil(t, env.L1)
}
if tt.wantL2Nodes != nil {
require.Len(t, env.L2, 1)
assert.Equal(t, tt.wantL2Nodes, env.L2[0].Nodes)
if tt.wantL2Services != nil {
assert.Equal(t, tt.wantL2Services, env.L2[0].Services)
}
} 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