The execution service is responsible for opening, managing terminal session, with the ability to send command and extract data. Service keeps tracking the session current directory commands, env variable setting, and will only issue SSH command if there is actual change.
Run the following command for exec service operation details:
endly -s=exec
endly -s=exec -a=run
endly -s=exec -a=extract
endly -s=exec -a=open
endly -s=exec -a=close
Service Id | Action | Description | Request | Response |
---|---|---|---|---|
exec | open | open SSH session on the target resource. | OpenSessionRequest | OpenSessionResponse |
exec | close | close SSH session | CloseSessionRequest | CloseSessionResponse |
exec | run | execute basic commands | RunRequest | RunResponse |
exec | extract | execute commands with ability to extract data, define error or success state | ExtractRequest | RunResponse |
RunRequest example
@run.json
{
"Target": {
"URL": "ssh://127.0.0.1/",
"Credentials": "${env.HOME}/.secret/localhost.json"
},
"SuperUser":true,
"Commands":["mkdir /tmp/app1"]
}
@run.yaml
target:
url: ssh://127.0.0.1/
credentials: ${env.HOME}/.secret/localhost.json
commands:
whoami
${cmd[0].stdout}:/root/? mkdir -p /tmp/app
${cmd[0].stdout}:!/root/? mkdir ~/app
echo cmd[0].stdout
ExtractRequest example
{
"Target": {
"URL": "ssh://127.0.0.1/",
"Credentials": "${env.HOME}/.secret/localhost.json"
},
"SystemPaths": ["/opt/sdk/go/bin"],
"Commands": [
{
"Command": "go version",
"Extraction": [
{
"RegExpr": "go(\\d\\.\\d)",
"Key": "Version"
}
]
}
]
}
In order to run any SSH command, service needs to open a session, it uses target.Credentials and secret service to connect the target host.
Opening session is an optional step, run or extract request will open session automatically.
By default session is open in non transient mode, which means once context.Close is called, session will be will be terminated. Otherwise caller is responsible for closing it.
manager := endly.New()
context := manager.NewContext(toolbox.NewContext())
target := url.NewResource("ssh://127.0.0.1", "~/.secret/localhost.json")
defer context.Close() // session closes as part of context.Close
response, err := manager.Run(context, exec.NewOpenSessionRequest(target, []string{"/usr/local/bin"}, map[string]string{"M2_HOME":"/users/test/.m2/"},false, "/"))
if err != nil {
log.Fatal(err)
}
openResponse := response.(*exec.OpenSessionResponse)
sessions :=context.TerminalSessions()
assert.True(t,sessions.Has(openResponse.SessionID))
log.Print(openResponse.SessionID)
Run vs Extract:
RunReuest provide a simple way to excute SSH command with conditional execution, it uses util.StdErrors as stdout errors. ExtractRequest has ability to fine tune SSH command execution with extraction data ability. Error matching in ExtractRequest does use any default value.
Command in RunRequest can represents one of the following:
- Simple command: i.e echo $HOME
- Conditional command: [criteria ?] command i.e. $stdout:/root/? echo 'hello root'",
manager := endly.New()
context := manager.NewContext(toolbox.NewContext())
var target= url.NewResource("ssh://127.0.0.1", "localhost")
var runRequest = exec.NewRunRequest(target, true, "whoami", "$stdout:/root/? echo 'hello root'")
var runResponse = &exec.RunResponse{}
err := endly.Run(context, runRequest, runResponse)
extractRequest := exec.NewExtractRequest(target,
exec.DefaultOptions(),
exec.NewExtractCommand(fmt.Sprintf("svn info"), "", nil, nil,
endly.NewDataExtraction("origin", "^URL:[\\t\\s]+([^\\s]+)", false),
endly.NewDataExtraction("revision", "Revision:\\s+([^\\s]+)", false)))
manager := endly.New()
context := manager.NewContext(toolbox.NewContext())
var runResponse := &exec.RunResponse{}
err := endly.Run(context, extractRequest, runResponse)
if err != nil {
log.Fatal(err)
}
This module provide SSH session recording ability to later replay it during unit testing without actual SSH involvement.
Recroding SSH session
To record actual SSH session use exec.OpenRecorderContext helper method, the last parameters specify location where conversation is recorded, actual dump takes place when context is closed (defer context.Clode()).
If you use sudo. any secret or credentials make sure that you rename it to *** before checking in any code so you can use var credentials, err = util.GetDummyCredential()
manager := endly.New()
target := url.NewResource("ssh://127.0.0.1", "~/.secret/localhost.json")
context, err := exec.NewSSHRecodingContext(manager, target, "test/session/context")
if err != nil {
log.Fatal(err)
}
defer context.Close()
Replaying SSH session
In order to replay previously recoded SSH session use exec.GetReplayService
helper method to create
a test SSHService, use location of stored SSH conversation as parameter, then create context with exec.OpenTestContext
manager := endly.New()
var credentials, err = util.GetDummyCredential()
if err != nil {
log.Fatal(err)
}
target := url.NewResource("ssh://127.0.0.1", credentials)
context, err := exec.NewSSHReplayContext(manager, target, "test/session/transient")
if err != nil {
log.Fatal(err)
}
response, err := manager.Run(context, exec.NewOpenSessionRequest(target, []string{"/usr/local/bin"}, map[string]string{"M2_HOME": "/users/test/.m2/"}, false, "/"))
if err != nil {
log.Fatal(err)
}