github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/cmd/state-exec/main.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"os/exec"
     8  	"runtime"
     9  	"time"
    10  
    11  	"github.com/ActiveState/cli/cmd/state-exec/internal/logr"
    12  )
    13  
    14  const (
    15  	executorName     = "state-exec"
    16  	envVarKeyVerbose = "ACTIVESTATE_VERBOSE"
    17  	userErrMsg       = "Not user serviceable; Please contact support for assistance."
    18  )
    19  
    20  var (
    21  	logErr = func(format string, args ...interface{}) {
    22  		fmt.Fprintf(os.Stderr, "%s: ", executorName)
    23  		fmt.Fprintf(os.Stderr, format+"\n", args...)
    24  	}
    25  )
    26  
    27  func logDbgFunc(start time.Time) logr.LogFunc {
    28  	return func(format string, args ...interface{}) {
    29  		fmt.Fprintf(os.Stderr, "[DEBUG %9d] ", time.Since(start).Nanoseconds())
    30  		fmt.Fprintf(os.Stderr, format+"\n", args...)
    31  	}
    32  }
    33  
    34  func init() {
    35  	// This application is not doing enough to warrant parallelism, so let's
    36  	// skip it and avoid the cost of scheduling.
    37  	runtime.GOMAXPROCS(1)
    38  }
    39  
    40  func main() {
    41  	if os.Getenv(envVarKeyVerbose) == "true" {
    42  		logr.SetDebug(logDbgFunc(time.Now()))
    43  	}
    44  
    45  	if err := run(); err != nil {
    46  		if exitErr := (&exec.ExitError{}); errors.As(err, &exitErr) {
    47  			os.Exit(exitErr.ExitCode())
    48  		}
    49  
    50  		logErr("run failed: %s", err)
    51  		logErr(userErrMsg)
    52  		os.Exit(1)
    53  	}
    54  
    55  	os.Exit(0)
    56  }
    57  
    58  func run() error {
    59  	logr.Debug("hello")
    60  	defer logr.Debug("run goodbye")
    61  
    62  	hb, err := newHeartbeat()
    63  	if err != nil {
    64  		return fmt.Errorf("cannot create new heartbeat: %w", err)
    65  	}
    66  	logr.Debug("message data - pid: %s, exec: %s", hb.ProcessID, hb.ExecPath)
    67  
    68  	meta, err := newExecutorMeta(hb.ExecPath)
    69  	if err != nil {
    70  		return fmt.Errorf("cannot create new executor meta: %w", err)
    71  	}
    72  	logr.CallIfDebugIsSet(func() {
    73  		logr.Debug("meta data - bins...")
    74  		for _, bin := range meta.Bins {
    75  			logr.Debug("            bins : %s", bin)
    76  		}
    77  	})
    78  	logr.Debug("meta data - matching bin: %s", meta.MatchingBin)
    79  	logr.CallIfDebugIsSet(func() {
    80  		logr.Debug("meta data - env...")
    81  		for _, entry := range meta.TransformedEnv {
    82  			logr.Debug("            env - kv: %s", entry)
    83  		}
    84  	})
    85  
    86  	logr.Debug("communications - sock: %s", meta.SockPath)
    87  	if err := sendMsgToService(meta.SockPath, hb); err != nil {
    88  		logr.Debug("                 sock - error: %v", err)
    89  
    90  		if inActiveStateCI() { // halt control flow on CI only
    91  			return fmt.Errorf("cannot send message to service (this error is handled in CI only): %w", err)
    92  		}
    93  	}
    94  
    95  	logr.Debug("cmd - running: %s", meta.MatchingBin)
    96  	exitCode, err := runCmd(meta)
    97  	if err != nil {
    98  		logr.Debug("      running - failed: bins (%v)", meta.ExecMeta.Bins)
    99  		return fmt.Errorf("cannot run command: %w", err)
   100  	}
   101  
   102  	msg, err := newExitCodeMessage(exitCode)
   103  	if err != nil {
   104  		return fmt.Errorf("cannot create new exit code message: %w", err)
   105  	}
   106  	logr.Debug("message data - exec: %s, exit code: %s", msg.ExecPath, msg.ExitCode)
   107  
   108  	if err := sendMsgToService(meta.SockPath, msg); err != nil {
   109  		logr.Debug("                 sock - error: %v", err)
   110  
   111  		if inActiveStateCI() { // halt control flow on CI only
   112  			return fmt.Errorf("cannot send message to service (this error is handled in CI only): %w", err)
   113  		}
   114  	}
   115  
   116  	return nil
   117  }