package
1.0.0
Repository: https://github.com/frigga-cloud/frigga-lib.git
Documentation: pkg.go.dev

# README

Runner

A runner is a custom go component, designed to help execute system commands, say ls -al |grep main in golang with better control and ease.

Properties

ParametersDatatypeAccessspecSample valueUsageMehtods associated
idstringprivateautogenerated"20ad0fe445e0fd6e597b7a1ac102a35e"Uniqie id of each unner instance.getState()
isConsoleboolprivateoptional-settingfalseIf set ture, system commands output will be printed on console. default=false (disabled.EnableConsole(), .DisableConsole), .getState()
timeoutintprivateoptional-setting9Runner will force terminate the system command execution if it exceeds seconds. ddefault=0 (no-timeout).SetTimeout(), .getState()
waitingPeriodintprivateoptional-setting3Runner will wait seconds before executing. default=0 (no delay).SetWaitingPeriod(), .getState()
logFilestringprivateoptional-setting"log.md"Running will write STDOUT of the system call in this file. default="" (file logging disabled).SetLogFile()
execuitedAtint64privatesystem-generated1693137765180789285The timestamp of the beginning of the command execution (epoch nanosecond). default=0.getState()
executionTimeNanoint64privatesystem-generated10001158509Execution time / duration of the system call (in nanoseconds). default=0.getState()
verificationPhrasestringprivateoptional-setting"Ready"User can provide a phrase/string. the runner will match this string in the output of the system call. if found then state=SUCCESSFUL. default="" (no verification).SetVerificationPhrase(), .getState()
statusstringprivatesystem-generated"PENDING"The current state of the runner. possible states: PENDING | RUNNING | COMPLETED | FAILED | TIMEDOUT | SUCCEEDED | KILLED. default=PENDING.GetStatus(), .GetState()
sysCmdstringprivatemandatory-input"ping google.com -c 4"The system command the user must provide to run.GetState(), .Execute()
logBuffer[]byteprivatesystem-generatedThe STDOUT content of the system call is stored here..Logs(), .ClearLogs()
cmd*exec.Cmdprivateinternal-usage
onLogCallbackfunc([]byte)privateoptional-callbackfunc logCallback (logLine []byte) {}If attached, an user-defined callback function will be called every time the system call generates a new line in STDOUT. the new line will be injected into the callback function.SetOnNewLineCallback()
onSuccessCallbackfunc([]byte)privateoptional-calbackfunc successCallback (logLine []byte) {}If attached, an user-defined callback function will be called if the user-provided verification phrase is found in the output of the system call..SetOnSuccessCallback()
onCompleteCallbackfunc([]byte)privateoptional-callbackfunc completeCallback (logLine []byte) {}If attached, an user-defined callback function will be called if and when the system call exits with exit-code=0 and verification is disabled.SetOnCompleteCallback()
onFailCallbackfunc([]byte)privateoptional-callbackfunc failCallback (logLine []byte) {}If attached, an user-defined callback function will be called if and when the system call exits with a non-zero status-code.SetOnFailCallback()
onTimeoutCallbackfunc([]byte)privateoptional-callbackfunc timeoutCallback (logLine []byte) {}If attached an user-defined callback function will be called if and when the system call times out.SetOnTimeoutCallback()

Methods

MethodsInput-descOutput-descFunction
func NewRunner() *Runner {}the system command thatrunner object instanceInstantiate a new runner object
func(r *Runner) EnableConsole() {}Enable console logging. if enabled, the system call's output will be printed to console
func(r *Runner) DisableConsole() {}Disable console login
func (r *Runner) SetTimeout(timeout int, callbacks ...func([]byte)) {}timeout in seconds, optionally timeout callback functionSet execution timeout (in seconds)  also optional timeout callback function
func(r *Runner) SetWaitingPeriod(wp int) {}waiting-period in secondsIf set runner will wait these many seconds before execuiting the system command
func (r *Runner) SetLogFile(file string) {}name of the log fileIf set, then the runner will create (or open) a log file with the provided name and will insert each command and its output in that file.
func (r *Runner) SetVerificationPhrase(phrase string, callbacks ...func([]byte)) {}a phrase "string" that is expected to be in the system call's output for the system call to be qualified as a success. Optionally one can pass a OnSuccess callback functionIf set, the runner will search the provided phrase in the system-call's output. If found then the state will become SUCCEEDED
func (r *Runner) OnLogCalback(callback func([]byte)) {}an user-defined functionIf attached, an user-defined function will be called every time the system call generates a new line. The new line will be injected into the attached function
func (r *Runner) OnSuccessCallback(callback func([]byte)) {}an user-defined functionIf attached, an user-defined callback function will be called if the user-provided verification phrase is found in the output of the system call.
func (r *Runner) OnCompleteCallback(callback func([]byte)) {}an user-defined functionIf attached, an user-defined callback function will be called if and when the system call exits with exit-code=0 and verification is disabled
func (r *Runner) OnFailCallback(callback func([]byte)) {}an user-defined functionIf attached, an user-defined callback function will be called if and when the system call exits with a non-zero status-code
func (r *Runner) OnTimeoutCallback(callback func([]byte)) {}an user-defined functionIf attached an user-defined callback function will be called if and when the system call times out
func (r *Runner) GetState() map[string]interface{} {}returns all the parametersIs used mostly for debugging
func (r *Runner) GetStatus() string {}returns the current status (e.g. RUNNING)Using this method one can chain multiple sequential commands with conditional branching. e.g. onSuccess() execute the next command and onFailure() retry
func (r *Runner) Execute(command, callbacks ...func([]byte)) ([]byte, error) {}command to execute, optionally OnFail callback, optional OnComplete callbacklog, errorThis is the main method that executes the system-call
func (r *Runner) Logs() string {}returns the output of the system callreturns the output of the system call
func (r *Runner) ClearLogs() {}This will clear the output of the system call
func (r *Runner) Kill() {}This will kill a running system call prematurely and on-demand

State diagram

The diagram bellow, illustrates different states the runner object can be in and what causes the state to change.

runner-state.drawio.png

Usage

Import gocomponents library

go get -u github.com/Frigga-Cloud/gocomponents/runner

Instantiate a new Runner object

runr = runner.NewRunner() 

Apply optional settings

runr.SetLogFile("log.md")                              // set log file name
runr.SetVerificationPhrase("main", successCallback)    // set verification phrase, optional onSuccess callback 
runr.EnableConsole()                                   // enable logs on console  
runr.SetWaitingPeriod(10)                              // set waiting period of 10s
runr.SetTimeout(5, timeoutCallback)                    // set timeout of 5s. Optional onTimeout callback

Optionally define a callback function that will be invoked every time there is a new log line generated by the system call. Also the new line will be injected into the callback function

func logCallback(logLine []byte) {
    fmt.Println("execution log: ", string(logLine))
}

Optionally, define other callback functions

// onSuccessCallback
func successCallback(stdout []byte) {
    fmt.Println("execuition successful: ", string(stdout))
}

// onFailCallback
func failCallback(stdout []byte) {
    fmt.Println("execuition failed: ", string(stdout))
}

// onCompleteCallback
func completeCallback(stdout []byte) {
    fmt.Println("execuition completed: ", string(stdout))
}

// onTimeoutCallback
func timeoutCallback(stdout []byte) {
    fmt.Println("execuition timedout: ", string(stdout))
}

Attach the above function as a Log-callback function to the runner

runr.SetOnNewLineCallback(logCallback)

Execute the runner with a custom command supplied as input argument

stdout, err := runr.Execute("ls -al | grep main", failCallback, completeCallback)

Execute in the background

go runr.Execute("ls -al")

Get current parameters

runr.GetState()

Output (sample)

{
    "console": false,
    "execuited_at": 1693137765180789285,
    "execution_time_nano": 10001158509,
    "file_path": "log.md",
    "id": "20ad0fe445e0fd6e597b7a1ac102a35e",
    "log_size_bytes": 956,
    "status": "TIMEDOUT",
    "system_command": "ping google.com -c 20",
    "timeout": 0,
    "verification_phrase": "",
    "waiting_period": 0
}

Get current status

runr.GetState()

Kill on demand

runr.Kill()

Example code

Here is a sample code with gin framework to showcase few use cases.



Test

Execute blocking

curl --location 'http://127.0.0.1:5000/exec'

Response

PING google.com (172.217.166.110) 56(84) bytes of data.
64 bytes from maa05s09-in-f14.1e100.net (172.217.166.110): icmp_seq=1 ttl=118 time=16.9 ms
64 bytes from maa05s09-in-f14.1e100.net (172.217.166.110): icmp_seq=2 ttl=118 time=26.7 ms
64 bytes from maa05s09-in-f14.1e100.net (172.217.166.110): icmp_seq=3 ttl=118 time=21.9 ms
64 bytes from maa05s09-in-f14.1e100.net (172.217.166.110): icmp_seq=4 ttl=118 time=22.9 ms

--- google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3003ms
rtt min/avg/max/mdev = 16.890/22.102/26.663/3.488 ms

Execute blocking with custom command payload

curl --location 'http://127.0.0.1:5000/exec-payload' \
--header 'Content-Type: application/json' \
--data '{
    "instruction": "ls -al |grep main"
}'

Response

-rw-r--r--  1 drag drag 2919 Aug 28 16:45 main.go

Execute async with custom payload

curl --location 'http://127.0.0.1:5000/exec-payload-async' \
--header 'Content-Type: application/json' \
--data '{
    "instruction": "tree ."
}'

Response

ok

Get current settings

curl --location 'http://127.0.0.1:5000/state'

Response

{
    "console": true,
    "execuited_at": 1693221850593927888,
    "execution_time_nano": 6292534,
    "file_path": "log.md",
    "id": "172ac965ab5c52e7d83a4af59dae77ba",
    "log_size_bytes": 93,
    "status": "SUCCEEDED",
    "system_command": "tree .",
    "timeout": 5,
    "verification_phrase": ".go",
    "waiting_period": 10
}

Get current status

curl --location 'http://127.0.0.1:5000/status'

Response

"SUCCEEDED"

Kill a ongoing system call

curl --location 'http://127.0.0.1:5000/kill'

Reponse

"KILLED"