ssh_commands_test.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. package nebula
  2. import (
  3. "bytes"
  4. "fmt"
  5. "path/filepath"
  6. "sort"
  7. "testing"
  8. "github.com/slackhq/nebula/commands"
  9. "github.com/slackhq/nebula/config"
  10. "github.com/slackhq/nebula/test"
  11. "github.com/stretchr/testify/assert"
  12. )
  13. // TestSSHCommandSurface is a characterization test for the ssh command surface.
  14. // It pins the registered command set, their descriptions, help text and flag definitions
  15. // so that moving the registry to a transport neutral package is provably a relocation
  16. // rather than a rewrite. Regenerate with `go test . -run TestSSHCommandSurface -update`.
  17. func TestSSHCommandSurface(t *testing.T) {
  18. got := renderCommandSurface(registeredCommands(t).Schema())
  19. test.Golden(t, filepath.Join("testdata", "ssh_commands.golden"), got)
  20. }
  21. // registeredCommands returns a registry holding every command nebula serves. The
  22. // Interface is never dereferenced: attachCommands only closes over it.
  23. func registeredCommands(t *testing.T) *commands.Registry {
  24. t.Helper()
  25. l := test.NewLogger()
  26. reg := commands.New()
  27. attachCommands(l, config.NewC(l), reg, &Interface{})
  28. return reg
  29. }
  30. // renderCommandSurface prints what a client actually sees, which is the schema rather
  31. // than the Command structs: the presentation flags are offered on every command that
  32. // returns a value, so reading Command.Flags alone would under-report the surface.
  33. func renderCommandSurface(cmds []commands.CommandSchema) string {
  34. lines := make([]string, 0, len(cmds))
  35. for _, cmd := range cmds {
  36. b := &bytes.Buffer{}
  37. fmt.Fprintf(b, "%s\n", cmd.Name)
  38. fmt.Fprintf(b, " short: %s\n", cmd.Short)
  39. fmt.Fprintf(b, " help: %s\n", cmd.Help)
  40. if len(cmd.Flags) > 0 {
  41. fmt.Fprintf(b, " flags:\n")
  42. for _, f := range cmd.Flags {
  43. kind := " string"
  44. if f.Bool {
  45. kind = ""
  46. }
  47. fmt.Fprintf(b, " -%s%s\n \t%s\n", f.Name, kind, f.Usage)
  48. }
  49. }
  50. lines = append(lines, b.String())
  51. }
  52. sort.Strings(lines)
  53. return joinLines(lines)
  54. }
  55. func joinLines(in []string) string {
  56. b := &bytes.Buffer{}
  57. for _, l := range in {
  58. b.WriteString(l)
  59. }
  60. return b.String()
  61. }
  62. // TestLocalSocketSurface pins which of nebula's commands the always-on local socket
  63. // serves: every one registered. Adding a command fails here until the list below says
  64. // so, which turns "somebody forgot to think about it" into a visible decision.
  65. func TestLocalSocketSurface(t *testing.T) {
  66. var served []commands.Name
  67. for _, c := range registeredCommands(t).All() {
  68. served = append(served, c.Name)
  69. }
  70. assert.ElementsMatch(t, []commands.Name{
  71. commands.Help,
  72. commands.Identity,
  73. commands.ListHostmap,
  74. commands.ListPendingHostmap,
  75. commands.ListLighthouseAddrmap,
  76. commands.PrintCert,
  77. commands.PrintTunnel,
  78. commands.PrintRelays,
  79. commands.DeviceInfo,
  80. commands.Version,
  81. commands.QueryLighthouse,
  82. commands.Reload,
  83. commands.LogLevel,
  84. commands.LogFormat,
  85. commands.ChangeRemote,
  86. commands.CloseTunnel,
  87. commands.CreateTunnel,
  88. commands.Whoami,
  89. }, served)
  90. }
  91. // TestInfoAPISurface pins which commands the info API serves. That surface may be open
  92. // to every user on the host, so a command joins it by declaring Identity, and this test
  93. // is where that declaration is reviewed: anything listed here hands whoever can reach
  94. // the socket what the command reports.
  95. func TestInfoAPISurface(t *testing.T) {
  96. reg := registeredCommands(t)
  97. var served []commands.Name
  98. for _, c := range reg.All() {
  99. if c.Identity {
  100. served = append(served, c.Name)
  101. }
  102. }
  103. assert.ElementsMatch(t, []commands.Name{commands.Identity, commands.Whoami}, served)
  104. }