diff options
| author | Shulhan <ms@kilabit.info> | 2023-10-30 19:31:06 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2023-10-30 19:31:06 +0700 |
| commit | 4726a28cee89f542eb3b0569639069a05d1df6b0 (patch) | |
| tree | 8ba5cd33f4f53387f183464088f98d556a9a7e45 | |
| parent | 5b6f533771e01e992e471d0e777d542c1526cf10 (diff) | |
| download | awwan-4726a28cee89f542eb3b0569639069a05d1df6b0.tar.xz | |
all: log all execution into file
For each script execution, a file suffixed with ".log" will be created
in the same directory with the same name as script file.
For example, if the script is path is "a/b/c.aww" then the log file
would named "a/b/c.aww.log".
This is to provides history and audit in the future.
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | awwan.go | 16 | ||||
| -rw-r--r-- | awwan_local_test.go | 20 | ||||
| -rw-r--r-- | awwan_play_test.go | 32 | ||||
| -rw-r--r-- | awwan_sudo_test.go | 12 | ||||
| -rw-r--r-- | cmd/awwan/main.go | 2 | ||||
| -rw-r--r-- | http_server.go | 7 | ||||
| -rw-r--r-- | request.go | 62 | ||||
| -rw-r--r-- | session.go | 4 | ||||
| -rw-r--r-- | testdata/local/local_encrypted.data | 6 | ||||
| -rw-r--r-- | testdata/local/local_test.data | 9 | ||||
| -rw-r--r-- | testdata/play/awwanssh.test/play_test.data | 6 |
12 files changed, 127 insertions, 50 deletions
@@ -1,4 +1,5 @@ *.lck +*.log /CHANGELOG.html /README.html /_AUR/*.tar.zst @@ -177,12 +177,6 @@ func (aww *Awwan) Local(req *Request) (err error) { sessionDir string ) - req.scriptPath = filepath.Clean(req.Script) - req.scriptPath, err = filepath.Abs(req.scriptPath) - if err != nil { - return fmt.Errorf("%s: %w", logp, err) - } - sessionDir = filepath.Dir(req.scriptPath) ses, err = NewSession(aww, sessionDir) @@ -210,7 +204,7 @@ func (aww *Awwan) Local(req *Request) (err error) { log.Printf(`%s: %s`, logp, errRemove) } - req.mlog.Flush() + req.close() }() var pos linePosition @@ -244,12 +238,6 @@ func (aww *Awwan) Play(req *Request) (err error) { sshSection *config.Section ) - req.scriptPath = filepath.Clean(req.Script) - req.scriptPath, err = filepath.Abs(req.scriptPath) - if err != nil { - return fmt.Errorf("%s: %w", logp, err) - } - sessionDir = filepath.Dir(req.scriptPath) ses, err = NewSession(aww, sessionDir) @@ -285,7 +273,7 @@ func (aww *Awwan) Play(req *Request) (err error) { defer func() { ses.sshc.rmdirAll(ses.sshc.dirTmp) - req.mlog.Flush() + req.close() }() var pos linePosition diff --git a/awwan_local_test.go b/awwan_local_test.go index f2a1de0..c18524c 100644 --- a/awwan_local_test.go +++ b/awwan_local_test.go @@ -47,7 +47,10 @@ func TestAwwanLocal(t *testing.T) { logw bytes.Buffer ) - req = NewRequest(CommandModeLocal, scriptFile, `1-`) + req, err = NewRequest(CommandModeLocal, scriptFile, `1-`) + if err != nil { + t.Fatal(err) + } req.registerLogWriter(`output`, &logw) @@ -131,7 +134,10 @@ func TestAwwanLocal_Get(t *testing.T) { var req *Request - req = NewRequest(CommandModeLocal, script, c.lineRange) + req, err = NewRequest(CommandModeLocal, script, c.lineRange) + if err != nil { + t.Fatal(err) + } err = aww.Local(req) if err != nil { @@ -251,7 +257,10 @@ func TestAwwanLocal_Put(t *testing.T) { var req *Request - req = NewRequest(CommandModeLocal, script, c.lineRange) + req, err = NewRequest(CommandModeLocal, script, c.lineRange) + if err != nil { + t.Fatal(err) + } err = aww.Local(req) if err != nil { @@ -350,7 +359,10 @@ func TestAwwanLocal_withEncryption(t *testing.T) { logw bytes.Buffer ) - req = NewRequest(CommandModeLocal, c.script, c.lineRange) + req, err = NewRequest(CommandModeLocal, c.script, c.lineRange) + if err != nil { + t.Fatal(err) + } req.registerLogWriter(`output`, &logw) diff --git a/awwan_play_test.go b/awwan_play_test.go index e73faf4..943ddab 100644 --- a/awwan_play_test.go +++ b/awwan_play_test.go @@ -51,11 +51,15 @@ func TestAwwan_Play_withLocal(t *testing.T) { } var ( - req = NewRequest(CommandModePlay, scriptFile, `1-`) - + req *Request logw bytes.Buffer ) + req, err = NewRequest(CommandModePlay, scriptFile, `1-`) + if err != nil { + t.Fatal(err) + } + req.registerLogWriter(`output`, &logw) err = aww.Play(req) @@ -108,6 +112,7 @@ func TestAwwan_Play_Get(t *testing.T) { }} var ( + req *Request c testCaseGetPut fi os.FileInfo gotContent []byte @@ -120,7 +125,10 @@ func TestAwwan_Play_Get(t *testing.T) { _ = os.Remove(c.fileDest) } - var req = NewRequest(CommandModePlay, scriptFile, c.lineRange) + req, err = NewRequest(CommandModePlay, scriptFile, c.lineRange) + if err != nil { + t.Fatal(err) + } err = aww.Play(req) if err != nil { @@ -191,6 +199,7 @@ func TestAwwan_Play_Put(t *testing.T) { }} var ( + req *Request c testCaseGetPut fi os.FileInfo gotContent []byte @@ -203,7 +212,10 @@ func TestAwwan_Play_Put(t *testing.T) { _ = os.Remove(c.fileDest) } - var req = NewRequest(CommandModePlay, scriptFile, c.lineRange) + req, err = NewRequest(CommandModePlay, scriptFile, c.lineRange) + if err != nil { + t.Fatal(err) + } err = aww.Play(req) if err != nil { @@ -280,6 +292,7 @@ func TestAwwan_Play_SudoGet(t *testing.T) { var ( mockin = &mockStdin{} + req *Request c testCaseGetPut fi os.FileInfo gotContent []byte @@ -292,7 +305,10 @@ func TestAwwan_Play_SudoGet(t *testing.T) { _ = os.Remove(c.fileDest) } - var req = NewRequest(CommandModePlay, scriptFile, c.lineRange) + req, err = NewRequest(CommandModePlay, scriptFile, c.lineRange) + if err != nil { + t.Fatal(err) + } // Mock the request stdin to read password from buffer. mockin.buf.Reset() @@ -369,6 +385,7 @@ func TestAwwan_Play_SudoPut(t *testing.T) { }} var ( + req *Request c testCaseGetPut fi os.FileInfo gotContent []byte @@ -381,7 +398,10 @@ func TestAwwan_Play_SudoPut(t *testing.T) { _ = os.Remove(c.fileDest) } - var req = NewRequest(CommandModePlay, scriptFile, c.lineRange) + req, err = NewRequest(CommandModePlay, scriptFile, c.lineRange) + if err != nil { + t.Fatal(err) + } err = aww.Play(req) if err != nil { diff --git a/awwan_sudo_test.go b/awwan_sudo_test.go index b4c9d9d..d73fe09 100644 --- a/awwan_sudo_test.go +++ b/awwan_sudo_test.go @@ -87,6 +87,7 @@ func TestAwwan_Local_SudoGet(t *testing.T) { script = filepath.Join(baseDir, `get.aww`) mockin = &mockStdin{} + req *Request c testCase gotContent []byte ) @@ -94,7 +95,10 @@ func TestAwwan_Local_SudoGet(t *testing.T) { for _, c = range cases { t.Log(c.desc) - var req = NewRequest(CommandModeLocal, script, c.lineRange) + req, err = NewRequest(CommandModeLocal, script, c.lineRange) + if err != nil { + t.Fatal(err) + } // Mock the request stdin to read password from buffer. mockin.buf.Reset() @@ -180,6 +184,7 @@ func TestAwwan_Local_SudoPut(t *testing.T) { mockTerm = mock.ReadWriter{} aww *Awwan + req *Request c testCase gotContent []byte ) @@ -200,7 +205,10 @@ func TestAwwan_Local_SudoPut(t *testing.T) { _ = os.Remove(c.fileDest) } - var req = NewRequest(CommandModeLocal, script, c.lineRange) + req, err = NewRequest(CommandModeLocal, script, c.lineRange) + if err != nil { + t.Fatal(err) + } mockin.buf.Reset() mockin.buf.WriteString(c.sudoPass) diff --git a/cmd/awwan/main.go b/cmd/awwan/main.go index b44b242..35c461a 100644 --- a/cmd/awwan/main.go +++ b/cmd/awwan/main.go @@ -150,7 +150,7 @@ func main() { return case awwan.CommandModeLocal, awwan.CommandModePlay: - req = awwan.NewRequest(cmdMode, flag.Arg(1), flag.Arg(2)) + req, err = awwan.NewRequest(cmdMode, flag.Arg(1), flag.Arg(2)) case awwan.CommandModeServe: if flag.NArg() <= 1 { diff --git a/http_server.go b/http_server.go index 4b667a4..d06647e 100644 --- a/http_server.go +++ b/http_server.go @@ -408,7 +408,12 @@ func (httpd *httpServer) awwanApiExecute(epr *libhttp.EndpointRequest) (resb []b req.Script = filepath.Join(httpd.memfsBase.Opts.Root, req.Script) req.lineRange = parseLineRange(req.LineRange) - req.init() + + err = req.init() + if err != nil { + res.Message = fmt.Sprintf(`%s: %s`, logp, err) + return nil, res + } var ( data = &HttpResponse{ @@ -4,12 +4,19 @@ package awwan import ( + "fmt" "io" "os" + "path/filepath" + "time" "github.com/shuLhan/share/lib/mlog" ) +// defLogTimeFormat define the default log time format. +// This is set as variable to make it easy overwriting it in testing. +var defLogTimeFormat = time.RFC3339 + // Request for executing local or remote script. // Each request define the Mode of execution, Script file to be executed, // and the lineRange -- list of line numbers to be executed. @@ -21,6 +28,10 @@ type Request struct { mlog *mlog.MultiLogger + // flog the log file where all input and output will be + // recorded. + flog *os.File + scriptPath string // The actual or cleaned up path of the Script. script *Script @@ -34,7 +45,7 @@ type Request struct { // NewRequest create new Request and initialize stdout and stderr to os.Stdout // and os.Stderr. -func NewRequest(mode, script, lineRange string) (req *Request) { +func NewRequest(mode, script, lineRange string) (req *Request, err error) { req = &Request{ Mode: mode, Script: script, @@ -42,23 +53,62 @@ func NewRequest(mode, script, lineRange string) (req *Request) { } req.lineRange = parseLineRange(lineRange) - req.init() - return req + err = req.init() + if err != nil { + return nil, fmt.Errorf(`NewRequest: %w`, err) + } + + return req, nil +} + +// close flush and release all resources. +func (req *Request) close() { + req.mlog.Flush() + + var err = req.flog.Sync() + if err != nil { + mlog.Errf(`%s`, err) + } + + err = req.flog.Close() + if err != nil { + mlog.Errf(`%s`, err) + } } -// init initialize all fields in Request. -func (req *Request) init() { +// init initialize multi loggers to write all output. +func (req *Request) init() (err error) { if req.mlog == nil { var ( namedStdout = mlog.NewNamedWriter(`stdout`, os.Stdout) namedStderr = mlog.NewNamedWriter(`stderr`, os.Stderr) ) - req.mlog = mlog.NewMultiLogger(``, ``, + + req.mlog = mlog.NewMultiLogger(defLogTimeFormat, filepath.Base(req.Script), []mlog.NamedWriter{namedStdout}, []mlog.NamedWriter{namedStderr}, ) } + + req.scriptPath = filepath.Clean(req.Script) + req.scriptPath, err = filepath.Abs(req.scriptPath) + if err != nil { + return err + } + + // Create log file to record all input and output for audit in the + // future. + var fileLog = req.scriptPath + `.log` + + req.flog, err = os.OpenFile(fileLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) + if err != nil { + return err + } + + req.registerLogWriter(`file`, req.flog) + + return nil } // registerLogWriter register a writer w to mlog output and error. @@ -453,7 +453,7 @@ func (ses *Session) executeScriptOnLocal(req *Request, pos linePosition) (err er continue } - req.mlog.Outf("\n--> local: %3d: %s", x, stmt.String()) + req.mlog.Outf(`--> local: %3d: %s`, x, stmt.String()) switch stmt.kind { case statementKindDefault: @@ -496,7 +496,7 @@ func (ses *Session) executeScriptOnRemote(req *Request, pos linePosition) (err e continue } - req.mlog.Outf("\n--> %s: %3d: %s", ses.sshc.conn, x, stmt.String()) + req.mlog.Outf(`--> %s: %3d: %s`, ses.sshc.conn, x, stmt.String()) switch stmt.kind { case statementKindDefault: diff --git a/testdata/local/local_encrypted.data b/testdata/local/local_encrypted.data index 5355762..2682124 100644 --- a/testdata/local/local_encrypted.data +++ b/testdata/local/local_encrypted.data @@ -1,6 +1,5 @@ <<< echo_encrypted - ---> local: 3: echo this_is_a_secret +- local_encrypted.aww --> local: 3: echo this_is_a_secret this_is_a_secret <<< echo_encrypted_no_pass @@ -10,6 +9,5 @@ Local: NewScript: ParseScript: template: local_encrypted.aww:3:7: executing "loc Local: NewSession: .awwan.env.vault: LoadPrivateKeyInteractive: x509: decryption password incorrect <<< sub_echo_encrypted - ---> local: 1: echo this_is_a_secret_in_sub +- local_encrypted.aww --> local: 1: echo this_is_a_secret_in_sub this_is_a_secret_in_sub diff --git a/testdata/local/local_test.data b/testdata/local/local_test.data index 8d2b0b9..d5bc811 100644 --- a/testdata/local/local_test.data +++ b/testdata/local/local_test.data @@ -1,11 +1,8 @@ Test on "local" command. <<< local:output - ---> local: 1: echo "hello" +- local.aww --> local: 1: echo "hello" hello - ---> local: 4: #local: echo "nothing" - ---> local: 6: echo "local" +- local.aww --> local: 4: #local: echo "nothing" +- local.aww --> local: 6: echo "local" local diff --git a/testdata/play/awwanssh.test/play_test.data b/testdata/play/awwanssh.test/play_test.data index 69840c6..9f27fae 100644 --- a/testdata/play/awwanssh.test/play_test.data +++ b/testdata/play/awwanssh.test/play_test.data @@ -3,9 +3,7 @@ Test on "play" command. <<< play_with_local:output --- SSH connection: awwanssh@127.0.0.1:10022 --- SSH identity file: [/home/awwan/.ssh/id_ed25519] - ---> awwanssh@127.0.0.1:10022: 2: pwd +- play.aww --> awwanssh@127.0.0.1:10022: 2: pwd /home/awwanssh - ---> awwanssh@127.0.0.1:10022: 5: #local: pwd +- play.aww --> awwanssh@127.0.0.1:10022: 5: #local: pwd /home/awwan/src |
