Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 34 additions & 5 deletions tool/tsh/common/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -1336,10 +1336,7 @@ func checkClusterSelection(cf *CLIConf, clusters types.KubeClusters, name string
query: cf.PredicateExpression,
}
if len(clusters) == 0 {
if selectors.IsEmpty() {
return trace.NotFound("no kubernetes clusters found, check 'tsh kube ls' for a list of known clusters")
}
return trace.NotFound("%v not found, check 'tsh kube ls' for a list of known clusters", selectors.String())
return trace.NotFound(formatKubeNotFound(cf.SiteName, selectors))
}
errMsg := formatAmbiguousKubeCluster(cf, selectors, clusters)
return trace.BadParameter(errMsg)
Expand All @@ -1358,18 +1355,40 @@ func matchClustersByName(nameOrPrefix string, clusters types.KubeClusters) types
if nameOrPrefix == "" {
return clusters
}
var prefixMatches types.KubeClusters

// look for an exact full name match.
for _, kc := range clusters {
if kc.GetName() == nameOrPrefix {
return types.KubeClusters{kc}
}
}

// or look for exact "discovered name" matches.
if clusters, ok := findKubeClustersByDiscoveredName(clusters, nameOrPrefix); ok {
return clusters
}

// or just filter by prefix.
var prefixMatches types.KubeClusters
for _, kc := range clusters {
if strings.HasPrefix(kc.GetName(), nameOrPrefix) {
prefixMatches = append(prefixMatches, kc)
}
}
return prefixMatches
}

func findKubeClustersByDiscoveredName(clusters types.KubeClusters, name string) (types.KubeClusters, bool) {
var out types.KubeClusters
for _, kc := range clusters {
discoveredName, ok := kc.GetLabel(types.DiscoveredNameLabel)
if ok && discoveredName == name {
out = append(out, kc)
}
}
return out, len(out) > 0
}

func (c *kubeLoginCommand) printUserMessage(cf *CLIConf, tc *client.TeleportClient, names []string) {
if tc.Profile().RequireKubeLocalProxy() {
c.printLocalProxyUserMessage(cf, names)
Expand Down Expand Up @@ -1690,6 +1709,16 @@ func formatAmbiguousKubeCluster(cf *CLIConf, selectors resourceSelectors, kubeCl
return formatAmbiguityErrTemplate(cf, selectors, listCommand, table, fullNameExample)
}

func formatKubeNotFound(clusterFlag string, selectors resourceSelectors) string {
listCmd := formatKubeListCommand(clusterFlag)
if selectors.IsEmpty() {
return fmt.Sprintf("no kubernetes clusters found, check '%v' for a list of known clusters",
listCmd)
}
return fmt.Sprintf("%v not found, check '%v' for a list of known clusters",
selectors, listCmd)
}

func formatKubeListCommand(clusterFlag string) string {
if clusterFlag == "" {
return "tsh kube ls"
Expand Down
49 changes: 48 additions & 1 deletion tool/tsh/common/kube_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,22 @@ import (
"path"
"strings"
"testing"
"time"

"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"

"github.com/gravitational/teleport/api/types"
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/api/utils/keypaths"
"github.com/gravitational/teleport/lib/kube/kubeconfig"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/srv/alpnproxy/common"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/tool/teleport/testenv"
Expand Down Expand Up @@ -94,7 +98,8 @@ func TestProxyKubeComplexSelectors(t *testing.T) {
testenv.WithResyncInterval(t, 0)
kubeFoo := "foo"
kubeFooBar := "foo-bar"
kubeBaz := "baz"
kubeBaz := "baz-qux"
kubeBazEKS := "baz-eks-us-west-1-123456789012"
kubeFooLeaf := "foo"
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
Expand All @@ -106,6 +111,9 @@ func TestProxyKubeComplexSelectors(t *testing.T) {
cfg.Kube.ListenAddr = utils.MustParseAddr(localListenerAddr())
cfg.Kube.KubeconfigPath = newKubeConfigFile(t, kubeFoo, kubeFooBar, kubeBaz)
cfg.Kube.StaticLabels = map[string]string{"env": "root"}
cfg.Kube.ResourceMatchers = []services.ResourceMatcher{{
Labels: map[string]apiutils.Strings{"*": {"*"}},
}}
}),
withLeafCluster(),
withLeafConfigFunc(
Expand All @@ -126,6 +134,34 @@ func TestProxyKubeComplexSelectors(t *testing.T) {
return len(rootClusters) == 3 && len(leafClusters) == 1
}),
)
// setup a fake "discovered" kube cluster by adding a discovered name label
// to a dynamic kube cluster.
kc, err := types.NewKubernetesClusterV3(
types.Metadata{
Name: kubeBazEKS,
Labels: map[string]string{
types.DiscoveredNameLabel: "baz",
types.OriginLabel: types.OriginDynamic,
},
},
types.KubernetesClusterSpecV3{
Kubeconfig: newKubeConfig(t, kubeBazEKS),
},
)
require.NoError(t, err)
err = s.root.GetAuthServer().CreateKubernetesCluster(ctx, kc)
require.NoError(t, err)
require.EventuallyWithT(t, func(c *assert.CollectT) {
servers, err := s.root.GetAuthServer().GetKubernetesServers(ctx)
assert.NoError(c, err)
for _, ks := range servers {
if ks.GetName() == kubeBazEKS {
return
}
}
assert.Fail(c, "kube server not found")
}, time.Second*10, time.Millisecond*500, "failed to find dynamically created kube cluster %v", kubeBazEKS)

rootClusterName := s.root.Config.Auth.ClusterName.GetClusterName()
leafClusterName := s.leaf.Config.Auth.ClusterName.GetClusterName()

Expand All @@ -146,6 +182,17 @@ func TestProxyKubeComplexSelectors(t *testing.T) {
},
args: []string{kubeFoo, "--insecure"},
},
{
desc: "with discovered name",
makeValidateCmdFn: func(t *testing.T) func(*exec.Cmd) error {
return func(cmd *exec.Cmd) error {
config := kubeConfigFromCmdEnv(t, cmd)
checkKubeLocalProxyConfig(t, s, config, rootClusterName, kubeBazEKS)
return nil
}
},
args: []string{"baz", "--insecure"},
},
{
desc: "with prefix name",
makeValidateCmdFn: func(t *testing.T) func(*exec.Cmd) error {
Expand Down
19 changes: 19 additions & 0 deletions tool/tsh/common/kube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,25 @@ func newKubeConfigFile(t *testing.T, clusterNames ...string) string {
return kubeConfigLocation
}

func newKubeConfig(t *testing.T, name string) []byte {
kubeConf := clientcmdapi.NewConfig()

kubeConf.Clusters[name] = &clientcmdapi.Cluster{
Server: newKubeSelfSubjectServer(t),
InsecureSkipTLSVerify: true,
}
kubeConf.AuthInfos[name] = &clientcmdapi.AuthInfo{}

kubeConf.Contexts[name] = &clientcmdapi.Context{
Cluster: name,
AuthInfo: name,
}

buf, err := clientcmd.Write(*kubeConf)
require.NoError(t, err)
return buf
}

func newKubeSelfSubjectServer(t *testing.T) string {
srv, err := kubeserver.NewKubeAPIMock()
require.NoError(t, err)
Expand Down