github.com/viant/toolbox@v0.34.5/ssh/replay_command.go (about)

     1  package ssh
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/viant/toolbox"
     6  	"io/ioutil"
     7  	"os"
     8  	"path"
     9  	"sort"
    10  	"strings"
    11  )
    12  
    13  //ReplayCommand represent a replay command
    14  type ReplayCommand struct {
    15  	Stdin  string
    16  	Index  int
    17  	Stdout []string
    18  	Error  string
    19  }
    20  
    21  //replayCommands represnets command grouped by stdin
    22  type ReplayCommands struct {
    23  	Commands map[string]*ReplayCommand
    24  	Keys     []string
    25  	BaseDir  string
    26  }
    27  
    28  //Register register stdin and corresponding stdout conversation
    29  func (c *ReplayCommands) Register(stdin, stdout string) {
    30  	if _, has := c.Commands[stdin]; !has {
    31  		c.Commands[stdin] = &ReplayCommand{
    32  			Stdin:  stdin,
    33  			Stdout: make([]string, 0),
    34  		}
    35  		c.Keys = append(c.Keys, stdin)
    36  	}
    37  	c.Commands[stdin].Stdout = append(c.Commands[stdin].Stdout, stdout)
    38  }
    39  
    40  //return stdout pointed by index and increases index or empty string if exhausted
    41  func (c *ReplayCommands) Next(stdin string) string {
    42  	var stdout = c.Commands[stdin].Stdout
    43  	var index = c.Commands[stdin].Index
    44  	if index < len(stdout) {
    45  		c.Commands[stdin].Index++
    46  		return stdout[index]
    47  	}
    48  	return ""
    49  }
    50  
    51  func (c *ReplayCommands) Enable(source interface{}) (err error) {
    52  	switch value := source.(type) {
    53  	case *service:
    54  		value.replayCommands = c
    55  		value.recordSession = true
    56  	case *multiCommandSession:
    57  		value.replayCommands = c
    58  		value.recordSession = true
    59  	default:
    60  		err = fmt.Errorf("unsupported type: %T", source)
    61  	}
    62  	return err
    63  }
    64  
    65  func (c *ReplayCommands) Disable(source interface{}) (err error) {
    66  	switch value := source.(type) {
    67  	case *service:
    68  		value.replayCommands = nil
    69  		value.recordSession = false
    70  	case *multiCommandSession:
    71  		value.replayCommands = nil
    72  		value.recordSession = false
    73  	default:
    74  		err = fmt.Errorf("unsupported type: %T", source)
    75  	}
    76  	return err
    77  }
    78  
    79  //Store stores replay command in the base directory
    80  func (c *ReplayCommands) Store() error {
    81  	err := toolbox.CreateDirIfNotExist(c.BaseDir)
    82  	if err != nil {
    83  		return err
    84  	}
    85  	for i, key := range c.Keys {
    86  		var command = c.Commands[key]
    87  		var filenamePrefix = path.Join(c.BaseDir, fmt.Sprintf("%03d", i+1))
    88  		var stdinFilename = filenamePrefix + "_000.stdin"
    89  		err := ioutil.WriteFile(stdinFilename, []byte(command.Stdin), 0644)
    90  		if err != nil {
    91  			return err
    92  		}
    93  		for j, stdout := range command.Stdout {
    94  			var stdoutFilename = fmt.Sprintf("%v_%03d.stdout", filenamePrefix, j+1)
    95  			err := ioutil.WriteFile(stdoutFilename, []byte(stdout), 0644)
    96  			if err != nil {
    97  				return err
    98  			}
    99  		}
   100  	}
   101  	return nil
   102  }
   103  
   104  //Load loads replay command from base directory
   105  func (c *ReplayCommands) Load() error {
   106  	parent, err := os.Open(c.BaseDir)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	files, err := parent.Readdir(1000)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	var stdinMap = make(map[string]string)
   115  	var stdoutMap = make(map[string]string)
   116  
   117  	for _, candidate := range files {
   118  		ext := path.Ext(candidate.Name())
   119  		var contentMap map[string]string
   120  		if ext == ".stdin" {
   121  			contentMap = stdinMap
   122  		} else if ext == ".stdout" {
   123  			contentMap = stdoutMap
   124  		} else {
   125  			continue
   126  		}
   127  		var filename = path.Join(c.BaseDir, candidate.Name())
   128  		content, err := ioutil.ReadFile(filename)
   129  		if err != nil {
   130  			return nil
   131  		}
   132  		contentMap[candidate.Name()] = string(content)
   133  	}
   134  
   135  	for key, stdin := range stdinMap {
   136  		var prefix = key[:len(key)-10]
   137  		var candidateKeys = toolbox.MapKeysToStringSlice(stdoutMap)
   138  		sort.Strings(candidateKeys)
   139  		for _, candidateKey := range candidateKeys {
   140  			if strings.HasPrefix(candidateKey, prefix) {
   141  				stdout := stdoutMap[candidateKey]
   142  				c.Register(stdin, stdout)
   143  			}
   144  		}
   145  	}
   146  	return nil
   147  }
   148  
   149  //Shell returns command shell
   150  func (c *ReplayCommands) Shell() string {
   151  	for _, candidate := range c.Commands {
   152  		if strings.HasPrefix(candidate.Stdin, "PS1=") && len(candidate.Stdout) > 0 {
   153  			return candidate.Stdout[0]
   154  		}
   155  	}
   156  	return ""
   157  }
   158  
   159  //System returns system name
   160  func (c *ReplayCommands) System() string {
   161  	for _, candidate := range c.Commands {
   162  		if strings.HasPrefix(candidate.Stdin, "uname -s") && len(candidate.Stdout) > 0 {
   163  			return strings.ToLower(candidate.Stdout[0])
   164  		}
   165  	}
   166  	return ""
   167  }
   168  
   169  //NewReplayCommands create a new replay commands or error if provided basedir does not exists and can not be created
   170  func NewReplayCommands(basedir string) (*ReplayCommands, error) {
   171  	if !toolbox.FileExists(basedir) {
   172  		err := os.MkdirAll(basedir, 0744)
   173  		if err != nil {
   174  			return nil, err
   175  		}
   176  	}
   177  	return &ReplayCommands{
   178  		Commands: make(map[string]*ReplayCommand),
   179  		Keys:     make([]string, 0),
   180  		BaseDir:  basedir,
   181  	}, nil
   182  }