...

Source file src/github.com/emissary-ingress/emissary/v3/pkg/busy/busy.go

Documentation: github.com/emissary-ingress/emissary/v3/pkg/busy

     1  // Package busy implements a dispatcher for BusyBox-style multi-call binaries.
     2  //
     3  // BUG(lukeshu): Global state is bad, but this package has global state in the form of the global
     4  // log level.
     5  package busy
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"os"
    11  	"path/filepath"
    12  	"sort"
    13  	"strconv"
    14  
    15  	//nolint:depguard // This is one of the few places where it is approrpiate to not go through
    16  	// to initialize dlog.
    17  	"github.com/sirupsen/logrus"
    18  
    19  	"github.com/datawire/dlib/dlog"
    20  )
    21  
    22  type Command struct {
    23  	Setup func(ctx context.Context)
    24  	Run   func(ctx context.Context, version string, args ...string) error
    25  }
    26  
    27  // logrusLogger is a global (rather than simply being a variable within the Main() function) for the
    28  // sole purpose of allowing the global program-wide log level to be fussed with at runtime.
    29  //
    30  // If you find yourself adding any other access to this blob of global state:
    31  //
    32  //	Stop.  You don't want more global state.  I (LukeShu) promise you there's a better way to do
    33  //	whatever you're attempting, and that adding more global state is not what you really want.
    34  var logrusLogger *logrus.Logger
    35  
    36  func init() {
    37  	testInit()
    38  }
    39  
    40  // testInit is separate from init() so that it can be explicitly called from the tests.
    41  func testInit() {
    42  	logrusLogger = logrus.New()
    43  	if useJSON, _ := strconv.ParseBool(os.Getenv("AMBASSADOR_JSON_LOGGING")); useJSON {
    44  		logrusLogger.SetFormatter(&logrus.JSONFormatter{
    45  			TimestampFormat: "2006-01-02 15:04:05.0000",
    46  		})
    47  	} else {
    48  		logrusLogger.SetFormatter(&logrus.TextFormatter{
    49  			TimestampFormat: "2006-01-02 15:04:05.0000",
    50  			FullTimestamp:   true,
    51  		})
    52  	}
    53  	logrusLogger.SetReportCaller(true)
    54  }
    55  
    56  // SetLogLevel sets the global program-wide log level.
    57  //
    58  // BUG(lukeshu): SetLogLevel mutates global state, and global state is bad.
    59  func SetLogLevel(lvl logrus.Level) {
    60  	logrusLogger.SetLevel(lvl)
    61  }
    62  
    63  // GetLogLevel gets the global program-wide log level.
    64  //
    65  // BUG(lukeshu): GetLogLevel accesses global state, and global state is bad.
    66  func GetLogLevel() logrus.Level {
    67  	return logrusLogger.GetLevel()
    68  }
    69  
    70  // Main should be called from your actual main() function.
    71  func Main(binName, humanName string, version string, cmds map[string]Command) {
    72  	name := filepath.Base(os.Args[0])
    73  	if name == binName && len(os.Args) > 1 {
    74  		name = os.Args[1]
    75  		os.Args = os.Args[1:]
    76  	}
    77  
    78  	logger := dlog.WrapLogrus(logrusLogger).
    79  		WithField("PID", os.Getpid()).
    80  		WithField("CMD", name)
    81  	ctx := dlog.WithLogger(context.Background(), logger) // early in Main()
    82  	dlog.SetFallbackLogger(logger.WithField("oops-i-did-not-pass-context-correctly", "THIS IS A BUG"))
    83  
    84  	if cmd, cmdOk := cmds[name]; cmdOk {
    85  		cmd.Setup(ctx)
    86  		if err := cmd.Run(ctx, version, os.Args[1:]...); err != nil {
    87  			dlog.Errorf(ctx, "shut down with error error: %v", err)
    88  			os.Exit(1)
    89  		}
    90  	} else {
    91  		fmt.Printf("The %s main program is a multi-call binary that combines various\n", humanName)
    92  		fmt.Println("support programs into one executable.")
    93  		fmt.Println()
    94  		fmt.Printf("Usage: %s <PROGRAM> [arguments]...\n", binName)
    95  		fmt.Println("   or: <PROGRAM> [arguments]...")
    96  		fmt.Println()
    97  		cmdnames := make([]string, 0, len(cmds))
    98  		for cmdname := range cmds {
    99  			cmdnames = append(cmdnames, cmdname)
   100  		}
   101  		sort.Strings(cmdnames)
   102  		fmt.Println("Available programs:", cmdnames)
   103  		fmt.Println()
   104  		fmt.Printf("Unknown program %q\n", name)
   105  		// POSIX says the shell should set $?=127 for "command
   106  		// not found", so non-shell programs that just run a
   107  		// command for you (including busybox) tend to mimic
   108  		// that and use exit code 127 to indicate "command not
   109  		// found".
   110  		os.Exit(127)
   111  	}
   112  }
   113  

View as plain text