Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Add start order for generic services #174

Merged
merged 3 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions modules/cli/cmd/devrunner/config/networks.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"

"github.com/astriaorg/astria-cli-go/modules/cli/cmd"
log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -212,19 +211,11 @@ func CreateNetworksConfig(binPath, configPath, localSequencerChainId, localNativ
// the default environment variables for the network configuration. It uses the
// BaseConfig to properly update the ASTRIA_COMPOSER_ROLLUPS env var.
func (n NetworkConfig) GetEndpointOverrides(bc BaseConfig) []string {
rollupEndpoint, exists := bc["astria_composer_rollups"]
rollups, exists := bc["astria_composer_rollups"]
if !exists {
log.Error("ASTRIA_COMPOSER_ROLLUPS not found in BaseConfig")
panic(fmt.Errorf("ASTRIA_COMPOSER_ROLLUPS not found in BaseConfig"))
}
// get the rollup ws endpoint
pattern := `ws{1,2}:\/\/.*:\d+`
re, err := regexp.Compile(pattern)
if err != nil {
log.Error("Error compiling regex")
panic(err)
}
match := re.FindString(rollupEndpoint)

return []string{
"ASTRIA_CONDUCTOR_SEQUENCER_GRPC_URL=" + n.SequencerGRPC,
Expand All @@ -233,6 +224,6 @@ func (n NetworkConfig) GetEndpointOverrides(bc BaseConfig) []string {
"ASTRIA_COMPOSER_SEQUENCER_CHAIN_ID=" + n.SequencerChainId,
"ASTRIA_COMPOSER_SEQUENCER_ABCI_ENDPOINT=" + n.SequencerRPC,
"ASTRIA_COMPOSER_SEQUENCER_GRPC_ENDPOINT=" + n.SequencerGRPC,
"ASTRIA_COMPOSER_ROLLUPS=" + n.RollupName + "::" + match,
"ASTRIA_COMPOSER_ROLLUPS=" + rollups,
}
}
15 changes: 15 additions & 0 deletions modules/cli/cmd/devrunner/config/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ type TUIConfig struct {
// Generic services start minimized
GenericStartsMinimized bool `mapstructure:"generic_starts_minimized" toml:"generic_starts_minimized"`

// Generic services start position relative to known services
GenericStartPosition string `mapstructure:"generic_start_position" toml:"generic_start_position"`

// Accessibility settings
HighlightColor string `mapstructure:"highlight_color" toml:"highlight_color"`
BorderColor string `mapstructure:"border_color" toml:"border_color"`
Expand All @@ -46,6 +49,7 @@ func DefaultTUIConfig() TUIConfig {
ComposerStartsMinimized: false,
SequencerStartsMinimized: false,
GenericStartsMinimized: true,
GenericStartPosition: "after",
HighlightColor: DefaultHighlightColor,
BorderColor: DefaultBorderColor,
}
Expand All @@ -63,6 +67,7 @@ func (c TUIConfig) String() string {
output += fmt.Sprintf("ComposerStartsMinimized: %v, ", c.ComposerStartsMinimized)
output += fmt.Sprintf("SequencerStartsMinimized: %v, ", c.SequencerStartsMinimized)
output += fmt.Sprintf("GenericStartsMinimized: %v", c.GenericStartsMinimized)
output += fmt.Sprintf("GenericStartPosition: %v", c.GenericStartPosition)
output += fmt.Sprintf("HighlightColor: %s, ", c.HighlightColor)
output += fmt.Sprintf("BorderColor: %s", c.BorderColor)
output += "}"
Expand All @@ -85,6 +90,16 @@ func LoadTUIConfigOrPanic(path string) TUIConfig {
panic(err)
}

// validate the generic start position value
switch config.GenericStartPosition {
case "before", "after", "default":
// valid values; do nothing
default:
log.Warnf("Invalid value for generic_start_position: %q. Valid values are: 'before', 'after', 'default'", config.GenericStartPosition)
log.Warnf("Setting generic_start_position to 'default'")
config.GenericStartPosition = "default"
}

return config
}

Expand Down
68 changes: 61 additions & 7 deletions modules/cli/cmd/devrunner/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package devrunner
import (
"context"
"fmt"
"net"
"net/http"
"path/filepath"
"strings"
Expand Down Expand Up @@ -138,14 +139,22 @@ func runCmdHandler(c *cobra.Command, _ []string) {
}
seqRunner = processrunner.NewProcessRunner(ctx, seqOpts)
case "composer":
compRCOpts := processrunner.ReadyCheckerOpts{
CallBackName: "Sequencer gRPC server is OK",
Callback: getComposerOKCallback(environment),
RetryCount: 10,
RetryInterval: 100 * time.Millisecond,
HaltIfFailed: false,
}
compReadinessCheck := processrunner.NewReadyChecker(compRCOpts)
composerPath := getFlagPath(c, "composer-path", "composer", service.LocalPath)
log.Debugf("arguments for composer service: %v", service.Args)
composerOpts := processrunner.NewProcessRunnerOpts{
Title: "Composer",
BinPath: composerPath,
Env: environment,
Args: service.Args,
ReadyCheck: nil,
ReadyCheck: &compReadinessCheck,
LogPath: filepath.Join(logsDir, appStartTime+"-astria-composer.log"),
ExportLogs: exportLogs,
StartMinimized: tuiConfig.ComposerStartsMinimized,
Expand Down Expand Up @@ -215,8 +224,17 @@ func runCmdHandler(c *cobra.Command, _ []string) {
}
}

// set the order of the services to start
allRunners := append([]processrunner.ProcessRunner{seqRunner, cometRunner, compRunner, condRunner}, genericRunners...)
// set the start order of the services
var allRunners []processrunner.ProcessRunner

switch tuiConfig.GenericStartPosition {
case "before":
allRunners = append(genericRunners, []processrunner.ProcessRunner{seqRunner, cometRunner, compRunner, condRunner}...)
case "after":
allRunners = append([]processrunner.ProcessRunner{seqRunner, cometRunner, compRunner, condRunner}, genericRunners...)
default:
allRunners = append([]processrunner.ProcessRunner{seqRunner, cometRunner, compRunner, condRunner}, genericRunners...)
}

runners, err := startProcessInOrder(ctx, allRunners...)
if err != nil {
Expand Down Expand Up @@ -252,10 +270,10 @@ func getFlagPath(c *cobra.Command, flag string, serviceName string, defaultValue
// is started by the sequencer is OK by making an HTTP request to the health
// endpoint. Being able to connect to the gRPC server is a requirement for both
// the Conductor and Composer services.
func getSequencerOKCallback(config []string) func() bool {
func getSequencerOKCallback(environment []string) func() bool {
// get the sequencer gRPC address from the environment
var seqGRPCAddr string
for _, envVar := range config {
for _, envVar := range environment {
if strings.HasPrefix(envVar, "ASTRIA_SEQUENCER_GRPC_ADDR") {
seqGRPCAddr = strings.Split(envVar, "=")[1]
break
Expand Down Expand Up @@ -289,10 +307,10 @@ func getSequencerOKCallback(config []string) func() bool {
// is started by Cometbft is OK by making an HTTP request to the health
// endpoint. Being able to connect to the rpc server is a requirement for both
// the Conductor and Composer services.
func getCometbftOKCallback(config []string) func() bool {
func getCometbftOKCallback(environment []string) func() bool {
// get the CometBFT rpc address from the environment
var seqRPCAddr string
for _, envVar := range config {
for _, envVar := range environment {
if strings.HasPrefix(envVar, "ASTRIA_CONDUCTOR_SEQUENCER_COMETBFT_URL") {
seqRPCAddr = strings.Split(envVar, "=")[1]
break
Expand Down Expand Up @@ -321,6 +339,42 @@ func getCometbftOKCallback(config []string) func() bool {
}
}

// getComposerOKCallback builds an anonymous function for use in a ProcessRunner
// ReadyChecker callback. The anonymous function checks if the rpc server that
// is started by Composer is OK by making an HTTP request to the health
// endpoint to confirm that the service and its rpc server have started.
func getComposerOKCallback(environment []string) func() bool {
// get the Composer rpc address from the environment
var composerRPCAddr string
for _, envVar := range environment {
if strings.HasPrefix(envVar, "ASTRIA_COMPOSER_GRPC_ADDR") {
composerRPCAddr = strings.Split(envVar, "=")[1]
break
}
}

// Split address into host and port
host, port, err := net.SplitHostPort(composerRPCAddr)
if err != nil {
log.WithError(err).Error("Failed to split address into host and port")
return func() bool { return false }
}

// return the anonymous callback function
return func() bool {
// Try to establish TCP connection
conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, port), 5*time.Second)
if err != nil {
log.WithError(err).Debug("Startup callback TCP connection to Composer failed")
return false
}
defer conn.Close()

log.Debug("Successfully established TCP connection to Composer")
return true
}
}

// startProcessInOrder starts the ProcessRunners in order they are provided, and
// returns an array of all successfully started services. It will skip any
// ProcessRunners that are nil. It will return an error if any of the
Expand Down
Loading