Skip to content

Commit

Permalink
Refactor how tsh lists apps as text
Browse files Browse the repository at this point in the history
This way it's going to be easier to add a column with target ports,
which should be displayed only if the list includes multi-port apps.

Conflicts:
	tool/tsh/common/app_test.go
	tool/tsh/common/tsh.go
  • Loading branch information
ravicious committed Dec 10, 2024
1 parent af06f54 commit 468ea19
Show file tree
Hide file tree
Showing 2 changed files with 284 additions and 52 deletions.
150 changes: 150 additions & 0 deletions tool/tsh/common/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/http/httputil"
"strings"
"testing"
"time"

Expand All @@ -41,6 +43,7 @@ import (
"github.com/gravitational/teleport/lib/reversetunnelclient"
"github.com/gravitational/teleport/lib/service"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/tlsca"
)

func startDummyHTTPServer(t *testing.T, name string) string {
Expand Down Expand Up @@ -484,3 +487,150 @@ uri: https://test-tp.teleport:8443
})
}
}

func TestWriteAppTable(t *testing.T) {
defaultAppListings := []appListing{
appListing{
Proxy: "example.com",
Cluster: "foo-cluster",
App: mustMakeNewAppV3(t, types.Metadata{Name: "root-app"}, types.AppSpecV3{
// Short URLs, because in tests the width of the term is just 80 characters and the public
// address column gets truncated very early.
PublicAddr: "https://root-app.example.com",
URI: "http://localhost:8080",
}),
},
appListing{
Proxy: "example.com",
Cluster: "bar-cluster",
App: mustMakeNewAppV3(t, types.Metadata{Name: "leaf-app"}, types.AppSpecV3{
PublicAddr: "https://leaf-app.example.com",
URI: "http://localhost:4242",
}),
},
}

tests := []struct {
name string
config appTableConfig
appListings []appListing
wantHeaders []string
wantNoHeaders []string
wantValues []string
wantNoValues []string
}{
{
name: "regular list",
config: appTableConfig{
active: []tlsca.RouteToApp{},
verbose: false,
listAll: false,
},
appListings: defaultAppListings,
wantHeaders: []string{"Application", "Public Address"},
// Public addresses are expected to be truncated when verbose mode is off.
wantValues: []string{"https://root-app...", "https://leaf-app...", "root-app", "leaf-app"},
wantNoHeaders: []string{"URI", "Proxy", "Cluster"},
wantNoValues: []string{"http://localhost:8080", "foo-cluster", "bar-cluster"},
},
{
name: "regular list with active app",
config: appTableConfig{
active: []tlsca.RouteToApp{
tlsca.RouteToApp{Name: "root-app"},
},
verbose: false,
listAll: false,
},
appListings: defaultAppListings,
wantHeaders: []string{"Application"},
wantValues: []string{"> root-app", "leaf-app"},
},
{
name: "regular list with no apps",
config: appTableConfig{
active: []tlsca.RouteToApp{},
verbose: false,
listAll: false,
},
appListings: []appListing{},
wantHeaders: []string{"Application", "Public Address"},
},
{
name: "verbose",
config: appTableConfig{
active: []tlsca.RouteToApp{},
verbose: true,
listAll: false,
},
appListings: defaultAppListings,
wantHeaders: []string{"URI", "Application", "Public Address"},
wantValues: []string{"http://localhost:8080", "http://localhost:4242",
"https://root-app.example.com", "https://leaf-app.example.com", "root-app", "leaf-app"},
wantNoHeaders: []string{"Proxy", "Cluster"},
wantNoValues: []string{"foo-cluster", "bar-cluster"},
},
{
name: "list all",
config: appTableConfig{
active: []tlsca.RouteToApp{},
verbose: false,
listAll: true,
},
appListings: defaultAppListings,
wantHeaders: []string{"Proxy", "Cluster", "Application", "Public Address"},
// Public addresses are expected to be truncated when verbose mode is off.
wantValues: []string{"foo-cluste...", "bar-cluste...", "example.co...", "https://ro...", "https://le...", "root-app", "leaf-app"},
wantNoHeaders: []string{"URI"},
wantNoValues: []string{"http://localhost:8080"},
},
{
name: "verbose and list all",
config: appTableConfig{
active: []tlsca.RouteToApp{},
verbose: true,
listAll: true,
},
appListings: defaultAppListings,
wantHeaders: []string{"Proxy", "Cluster", "URI", "Application", "Public Address"},
wantValues: []string{"foo-cluster", "bar-cluster", "http://localhost:8080", "http://localhost:4242",
"https://root-app.example.com", "https://leaf-app.example.com", "root-app", "leaf-app"},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var b bytes.Buffer
w := io.Writer(&b)

err := writeAppTable(w, test.appListings, test.config)
require.NoError(t, err)

lines := strings.SplitN(b.String(), "\n", 3)
headers := lines[0]
// The second line contains header separators ("------"), that's why it's skipped here.
values := lines[2]

for _, wantHeader := range test.wantHeaders {
assert.Contains(t, headers, wantHeader)
}
for _, wantNoHeader := range test.wantNoHeaders {
assert.NotContains(t, headers, wantNoHeader)
}

for _, wantValue := range test.wantValues {
assert.Contains(t, values, wantValue)
}
for _, wantNoValue := range test.wantNoValues {
assert.NotContains(t, values, wantNoValue)
}
})
}
}

func mustMakeNewAppV3(t *testing.T, meta types.Metadata, spec types.AppSpecV3) *types.AppV3 {
t.Helper()
app, err := types.NewAppV3(meta, spec)
require.NoError(t, err)
return app
}
Loading

0 comments on commit 468ea19

Please sign in to comment.