github.com/filecoin-project/bacalhau@v0.3.23-0.20230228154132-45c989550ace/cmd/bacalhau/root.go (about)

     1  package bacalhau
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"os/signal"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/filecoin-project/bacalhau/pkg/config"
    12  	"github.com/filecoin-project/bacalhau/pkg/logger"
    13  	"github.com/filecoin-project/bacalhau/pkg/system"
    14  	"github.com/filecoin-project/bacalhau/pkg/telemetry"
    15  	"github.com/filecoin-project/bacalhau/pkg/version"
    16  	"github.com/rs/zerolog/log"
    17  	"github.com/spf13/cobra"
    18  	"github.com/spf13/viper"
    19  	"go.opentelemetry.io/otel/trace"
    20  )
    21  
    22  var apiHost string
    23  var apiPort int
    24  var doNotTrack bool
    25  
    26  var loggingMode = logger.LogModeDefault
    27  
    28  var Fatal = FatalErrorHandler
    29  
    30  var defaultAPIHost string
    31  var defaultAPIPort int
    32  
    33  func init() { //nolint:gochecknoinits
    34  	defaultAPIHost = system.Envs[system.GetEnvironment()].APIHost
    35  	defaultAPIPort = system.Envs[system.GetEnvironment()].APIPort
    36  
    37  	if config.GetAPIHost() != "" {
    38  		defaultAPIHost = config.GetAPIHost()
    39  	}
    40  
    41  	if config.GetAPIPort() != "" {
    42  		intPort, err := strconv.Atoi(config.GetAPIPort())
    43  		if err == nil {
    44  			defaultAPIPort = intPort
    45  		}
    46  	}
    47  
    48  	if logtype, set := os.LookupEnv("LOG_TYPE"); set {
    49  		loggingMode = logger.LogMode(strings.ToLower(logtype))
    50  	}
    51  
    52  	// Force cobra to set apiHost & apiPort
    53  	NewRootCmd()
    54  }
    55  
    56  func NewRootCmd() *cobra.Command {
    57  	RootCmd := &cobra.Command{
    58  		Use:   getCommandLineExecutable(),
    59  		Short: "Compute over data",
    60  		Long:  `Compute over data`,
    61  		PersistentPreRun: func(cmd *cobra.Command, args []string) {
    62  			ctx := cmd.Context()
    63  
    64  			logger.ConfigureLogging(loggingMode)
    65  
    66  			cm := system.NewCleanupManager()
    67  			cm.RegisterCallback(telemetry.Cleanup)
    68  			ctx = context.WithValue(ctx, systemManagerKey, cm)
    69  
    70  			var names []string
    71  			root := cmd
    72  			for ; root.HasParent(); root = root.Parent() {
    73  				names = append([]string{root.Name()}, names...)
    74  			}
    75  			name := fmt.Sprintf("bacalhau.%s", strings.Join(names, "."))
    76  			ctx, span := system.NewRootSpan(ctx, system.GetTracer(), name)
    77  			ctx = context.WithValue(ctx, spanKey, span)
    78  
    79  			cmd.SetContext(ctx)
    80  		},
    81  		PersistentPostRun: func(cmd *cobra.Command, args []string) {
    82  			ctx := cmd.Context()
    83  			ctx.Value(spanKey).(trace.Span).End()
    84  			ctx.Value(systemManagerKey).(*system.CleanupManager).Cleanup(ctx)
    85  		},
    86  	}
    87  	// ====== Start a job
    88  
    89  	// Create job from file
    90  	RootCmd.AddCommand(newCreateCmd())
    91  
    92  	// Plumbing commands (advanced usage)
    93  	RootCmd.AddCommand(newDockerCmd())
    94  	RootCmd.AddCommand(newWasmCmd())
    95  
    96  	// Porcelain commands (language specific easy to use commands)
    97  	RootCmd.AddCommand(newRunCmd())
    98  
    99  	RootCmd.AddCommand(newValidateCmd())
   100  
   101  	RootCmd.AddCommand(newVersionCmd())
   102  
   103  	// ====== Get information or results about a job
   104  	// Describe a job
   105  	RootCmd.AddCommand(newDescribeCmd())
   106  
   107  	// Get the results of a job
   108  	RootCmd.AddCommand(newGetCmd())
   109  
   110  	// Cancel a job
   111  	RootCmd.AddCommand(newCancelCmd())
   112  
   113  	// List jobs
   114  	RootCmd.AddCommand(newListCmd())
   115  
   116  	// ====== Run a server
   117  
   118  	// Serve commands
   119  	RootCmd.AddCommand(newServeCmd())
   120  	RootCmd.AddCommand(newSimulatorCmd())
   121  	RootCmd.AddCommand(newIDCmd())
   122  	RootCmd.AddCommand(newDevStackCmd())
   123  
   124  	RootCmd.PersistentFlags().StringVar(
   125  		&apiHost, "api-host", defaultAPIHost,
   126  		`The host for the client and server to communicate on (via REST).
   127  Ignored if BACALHAU_API_HOST environment variable is set.`,
   128  	)
   129  	RootCmd.PersistentFlags().IntVar(
   130  		&apiPort, "api-port", defaultAPIPort,
   131  		`The port for the client and server to communicate on (via REST).
   132  Ignored if BACALHAU_API_PORT environment variable is set.`,
   133  	)
   134  	RootCmd.PersistentFlags().Var(
   135  		LoggingFlag(&loggingMode), "log-mode",
   136  		`Log format: 'default','station','json','combined','event'`,
   137  	)
   138  	return RootCmd
   139  }
   140  
   141  func Execute() {
   142  	rootCmd := NewRootCmd()
   143  
   144  	// Ensure commands are able to stop cleanly if someone presses ctrl+c
   145  	ctx, cancel := signal.NotifyContext(context.Background(), ShutdownSignals...)
   146  	defer cancel()
   147  	rootCmd.SetContext(ctx)
   148  
   149  	doNotTrack = false
   150  	if doNotTrackValue, foundDoNotTrack := os.LookupEnv("DO_NOT_TRACK"); foundDoNotTrack {
   151  		doNotTrackInt, err := strconv.Atoi(doNotTrackValue)
   152  		if err == nil && doNotTrackInt == 1 {
   153  			doNotTrack = true
   154  		}
   155  	}
   156  
   157  	viper.SetEnvPrefix("BACALHAU")
   158  
   159  	if err := viper.BindEnv("API_HOST"); err != nil {
   160  		log.Ctx(ctx).Fatal().Msgf("API_HOST was set, but could not bind.")
   161  	}
   162  
   163  	if err := viper.BindEnv("API_PORT"); err != nil {
   164  		log.Ctx(ctx).Fatal().Msgf("API_PORT was set, but could not bind.")
   165  	}
   166  
   167  	viper.AutomaticEnv()
   168  
   169  	if envAPIHost := viper.GetString("API_HOST"); envAPIHost != "" {
   170  		apiHost = envAPIHost
   171  	}
   172  
   173  	if envAPIPort := viper.GetString("API_PORT"); envAPIPort != "" {
   174  		var parseErr error
   175  		apiPort, parseErr = strconv.Atoi(envAPIPort)
   176  		if parseErr != nil {
   177  			log.Ctx(ctx).Fatal().Msgf("could not parse API_PORT into an int. %s", envAPIPort)
   178  		}
   179  	}
   180  
   181  	// Use stdout, not stderr for cmd.Print output, so that
   182  	// e.g. ID=$(bacalhau run) works
   183  	rootCmd.SetOut(system.Stdout)
   184  	// TODO this is from fixing a deprecation warning for SetOutput. Shouldn't this be system.Stderr?
   185  	rootCmd.SetErr(system.Stdout)
   186  
   187  	if err := rootCmd.Execute(); err != nil {
   188  		Fatal(rootCmd, err.Error(), 1)
   189  	}
   190  }
   191  
   192  type contextKey struct {
   193  	name string
   194  }
   195  
   196  var systemManagerKey = contextKey{name: "context key for storing the system manager"}
   197  var spanKey = contextKey{name: "context key for storing the root span"}
   198  
   199  func checkVersion(cmd *cobra.Command, args []string) error {
   200  	ctx := cmd.Context()
   201  
   202  	// corba doesn't do PersistentPreRun{,E} chaining yet
   203  	// https://github.com/spf13/cobra/issues/252
   204  	root := cmd
   205  	for ; root.HasParent(); root = root.Parent() {
   206  	}
   207  	root.PersistentPreRun(cmd, args)
   208  
   209  	// Check that the server version is compatible with the client version
   210  	serverVersion, _ := GetAPIClient().Version(ctx) // Ok if this fails, version validation will skip
   211  	if err := ensureValidVersion(ctx, version.Get(), serverVersion); err != nil {
   212  		Fatal(cmd, fmt.Sprintf("version validation failed: %s", err), 1)
   213  		return err
   214  	}
   215  
   216  	return nil
   217  }