ssh_commands_test.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  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. Commands are served by default, so this test is what turns "somebody forgot to
  64. // think about it" into a visible decision: adding a command fails here until the lists
  65. // below say where it belongs.
  66. func TestLocalSocketSurface(t *testing.T) {
  67. reg := registeredCommands(t)
  68. var served, withheld []commands.Name
  69. for _, c := range reg.All() {
  70. if c.ForProfiling {
  71. withheld = append(withheld, c.Name)
  72. } else {
  73. served = append(served, c.Name)
  74. }
  75. }
  76. // Nothing is withheld today: the profiling commands, which were, are gone.
  77. assert.Empty(t, withheld)
  78. assert.ElementsMatch(t, []commands.Name{
  79. commands.Help,
  80. commands.Identity,
  81. commands.ListHostmap,
  82. commands.ListPendingHostmap,
  83. commands.ListLighthouseAddrmap,
  84. commands.PrintCert,
  85. commands.PrintTunnel,
  86. commands.PrintRelays,
  87. commands.DeviceInfo,
  88. commands.Version,
  89. commands.QueryLighthouse,
  90. commands.Reload,
  91. commands.LogLevel,
  92. commands.LogFormat,
  93. commands.ChangeRemote,
  94. commands.CloseTunnel,
  95. commands.CreateTunnel,
  96. commands.Whoami,
  97. }, served)
  98. }
  99. // TestInfoAPISurface pins which commands the info API serves. That surface may be open
  100. // to every user on the host, so a command joins it by declaring Identity, and this test
  101. // is where that declaration is reviewed: anything listed here hands whoever can reach
  102. // the socket what the command reports.
  103. func TestInfoAPISurface(t *testing.T) {
  104. reg := registeredCommands(t)
  105. var served []commands.Name
  106. for _, c := range reg.All() {
  107. if c.Identity {
  108. served = append(served, c.Name)
  109. }
  110. }
  111. assert.ElementsMatch(t, []commands.Name{commands.Identity, commands.Whoami}, served)
  112. }