Skip to content

Commit

Permalink
Finish file output (#10)
Browse files Browse the repository at this point in the history
* Added host output to file

* Updated README
  • Loading branch information
DiscoRiver authored Apr 16, 2022
1 parent 11861be commit faac7fe
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 5 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@ This program was developed alongside https://github.com/discoriver/massh, and is
- Ability for scripts to be executed.
- Ability to retry failed hosts a configurable number of times.
- Better SSH key handling.
- Omnivore will eventually offload in-memory storage of host output to files within `~/.omnivore/history/`, however we need to build a system for preserving in-memory functionality as a fallback if writing fails, to avoid crashing completely.

## Known Issues

- If using iTerm2 on MacOS, you will need to edit your profile, and under the `Terminal` heading, uncheck the option `Save lines to scrollback in alternate screen mode`. Leaving this option checked will cause major trauma to your scroll buffer when resizing the terminal window when Omnivore is running.

## Noteworthy

- Application logs are located at `~/.omnivore/history/`. There is a timestamped directory for each run of omnivore, containing a file for each host containing it's output, and a log file for that run.

## Testing

Testing group should be appended to all tests, either `UnitWorkflow` or `IntegrationWorkflow`.
Expand Down
4 changes: 3 additions & 1 deletion internal/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,7 @@ func (o *OmniLogger) Init() {
o.er = log.New(o.FileOutput, "", log.Ldate|log.Lmicroseconds)
o.fatal = log.New(o.FileOutput, "", log.Ldate|log.Lmicroseconds)

o.info.Println("Omnivore Started.")
o.info.Println("-----------------------------------------------")
o.info.Println("************** Omnivore Started. **************")
o.info.Println("-----------------------------------------------")
}
16 changes: 14 additions & 2 deletions internal/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package store

import (
"fmt"
ovlog "github.com/discoriver/omnivore/internal/log"
"github.com/discoriver/omnivore/internal/path"
"github.com/discoriver/omnivore/pkg/group"
"log"
Expand All @@ -18,6 +19,9 @@ var (

// Session is initalised with NewStorageSession, used for file operations.
Session *StorageSession

// Trouble writing host output?
hostFileWriteFailure bool
)

// StorageSession holds directory information about the current application run state.
Expand Down Expand Up @@ -96,7 +100,15 @@ func (s *StorageSession) InitHostDir(name string) {

// WriteOutputFileForHost writes the content of an identifying pair to a file, for future processing out of memory. The identifying pair's key should always be the hostname.
func (s *StorageSession) WriteOutputFileForHost(idp *group.IdentifyingPair) {
// Key should always be be the hostname.
//filePath := s.SessionDir + string(os.PathSeparator) + idp.Key
// Key should always be the hostname in Omnivore
filePath := s.SessionDir + string(os.PathSeparator) + idp.Key

// Don't log fatal here, as the application can still be allowed to function in-memory.
if err := os.WriteFile(filePath, idp.Value, defaultDirPermissions); err != nil {
ovlog.OmniLog.Warn("Couldn't write host output file: %s", err.Error())

// TODO: Have application continue to run in-memory if writing these files fails.
hostFileWriteFailure = true
}
ovlog.OmniLog.Info("Written host output file for %s", idp.Key)
}
13 changes: 11 additions & 2 deletions omnivore/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package omnivore

import (
"fmt"
"github.com/discoriver/omnivore/internal/store"
"github.com/discoriver/omnivore/internal/ui"
"github.com/discoriver/omnivore/pkg/group"
"sync"
Expand Down Expand Up @@ -70,9 +71,15 @@ func OmniRun(cmd *OmniCommandFlags, safe chan struct{}, uiStarted chan struct{})
go func() {
if k.Error != nil {
// Group similar errors (these are package errors, not ssh Stderr)
ui.DP.Group.AddToGroup(group.NewIdentifyingPair(k.Host, []byte(k.Error.Error())))
hostErrOutput := group.NewIdentifyingPair(k.Host, []byte(k.Error.Error()))
ui.DP.Group.AddToGroup(hostErrOutput)
ui.DP.StreamCycle.AddFailedHost(k.Host)

// This will print package errors, which includes dial errors to the host, so we include it here
// as it's "technically" output, and is displayed on the UI. Although not strictly stderr,
// it is still a response from interacting with the SSH protocol.
store.Session.WriteOutputFileForHost(hostErrOutput)

wg.Done()
} else {
readStreamWithTimeout(k, time.Duration(cmd.CommandTimeout), ui.DP.Group, &wg)
Expand Down Expand Up @@ -119,8 +126,10 @@ func readStreamWithTimeout(res massh.Result, t time.Duration, grp *group.ValueGr
// Confirm that the host has exited.
log.OmniLog.Info("Host %s finished.", res.Host)
timer.Reset(timeout)
grp.AddToGroup(group.NewIdentifyingPair(res.Host, bes))
hostOutputPair := group.NewIdentifyingPair(res.Host, bes)
grp.AddToGroup(hostOutputPair)
ui.DP.StreamCycle.AddCompletedHost(res.Host)
store.Session.WriteOutputFileForHost(hostOutputPair)
return
case <-timer.C:
grp.AddToGroup(group.NewIdentifyingPair(res.Host, []byte("Activity timeout.")))
Expand Down

0 comments on commit faac7fe

Please sign in to comment.