mirror of
https://github.com/gravitational/teleport
synced 2024-10-21 09:44:51 +00:00
Add unit tests for kubeconfig updates
Several tests to confirm correctness of kubeconfig update logic. Specifically - to make sure existing configuration is not deleted. `UpdateKubeconfig` was split into two functions because mocking `*client.TeleportClient` was really difficult. Fixes #3209
This commit is contained in:
parent
12952b4904
commit
8c75759b98
|
@ -1,6 +1,7 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
@ -23,22 +24,21 @@ var log = logrus.WithFields(logrus.Fields{
|
|||
|
||||
// UpdateKubeconfig adds Teleport configuration to kubeconfig.
|
||||
func UpdateKubeconfig(tc *client.TeleportClient) error {
|
||||
config, err := LoadKubeConfig()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
clusterName, proxyPort := tc.KubeProxyHostPort()
|
||||
clusterAddr := fmt.Sprintf("https://%v:%v", clusterName, proxyPort)
|
||||
if tc.SiteName != "" {
|
||||
clusterName = tc.SiteName
|
||||
}
|
||||
|
||||
creds, err := tc.LocalAgent().GetKey()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
certAuthorities, err := tc.LocalAgent().GetCertsPEM()
|
||||
|
||||
return updateKubeconfigWithValues(clusterName, clusterAddr, creds)
|
||||
}
|
||||
|
||||
func updateKubeconfigWithValues(clusterName, clusterAddr string, creds *client.Key) error {
|
||||
config, err := LoadKubeConfig()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ func UpdateKubeconfig(tc *client.TeleportClient) error {
|
|||
}
|
||||
config.Clusters[clusterName] = &clientcmdapi.Cluster{
|
||||
Server: clusterAddr,
|
||||
CertificateAuthorityData: certAuthorities,
|
||||
CertificateAuthorityData: bytes.Join(creds.TLSCAs(), []byte("\n")),
|
||||
}
|
||||
|
||||
lastContext := config.Contexts[clusterName]
|
||||
|
|
242
lib/kube/client/kubeclient_test.go
Normal file
242
lib/kube/client/kubeclient_test.go
Normal file
|
@ -0,0 +1,242 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/lib/auth"
|
||||
"github.com/gravitational/teleport/lib/auth/testauthority"
|
||||
"github.com/gravitational/teleport/lib/client"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/sshutils"
|
||||
"github.com/gravitational/teleport/lib/tlsca"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/jonboulle/clockwork"
|
||||
"gopkg.in/check.v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
func TestKubeClient(t *testing.T) { check.TestingT(t) }
|
||||
|
||||
type KubeconfigSuite struct {
|
||||
kubeconfigPath string
|
||||
initialConfig clientcmdapi.Config
|
||||
}
|
||||
|
||||
var _ = check.Suite(&KubeconfigSuite{})
|
||||
|
||||
func (s *KubeconfigSuite) SetUpTest(c *check.C) {
|
||||
f, err := ioutil.TempFile("", "kubeconfig")
|
||||
if err != nil {
|
||||
c.Fatalf("failed to create temp kubeconfig file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Note: LocationOfOrigin and Extensions would be automatically added on
|
||||
// clientcmd.Write below. Set them explicitly so we can compare
|
||||
// s.initialConfig against loaded config.
|
||||
//
|
||||
// TODO: use a comparison library that can ignore individual fields.
|
||||
s.initialConfig = clientcmdapi.Config{
|
||||
CurrentContext: "dev",
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"cluster-1": {
|
||||
CertificateAuthority: "fake-ca-file",
|
||||
Server: "https://1.2.3.4",
|
||||
LocationOfOrigin: f.Name(),
|
||||
Extensions: map[string]runtime.Object{},
|
||||
},
|
||||
"cluster-2": {
|
||||
InsecureSkipTLSVerify: true,
|
||||
Server: "https://1.2.3.5",
|
||||
LocationOfOrigin: f.Name(),
|
||||
Extensions: map[string]runtime.Object{},
|
||||
},
|
||||
},
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"developer": {
|
||||
ClientCertificate: "fake-client-cert",
|
||||
ClientKey: "fake-client-key",
|
||||
LocationOfOrigin: f.Name(),
|
||||
Extensions: map[string]runtime.Object{},
|
||||
},
|
||||
"admin": {
|
||||
Username: "admin",
|
||||
Password: "hunter1",
|
||||
LocationOfOrigin: f.Name(),
|
||||
Extensions: map[string]runtime.Object{},
|
||||
},
|
||||
"support": {
|
||||
Exec: &clientcmdapi.ExecConfig{
|
||||
Command: "/bin/get_creds",
|
||||
Args: []string{"--role=support"},
|
||||
},
|
||||
LocationOfOrigin: f.Name(),
|
||||
Extensions: map[string]runtime.Object{},
|
||||
},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"dev": {
|
||||
Cluster: "cluster-2",
|
||||
AuthInfo: "developer",
|
||||
LocationOfOrigin: f.Name(),
|
||||
Extensions: map[string]runtime.Object{},
|
||||
},
|
||||
"prod": {
|
||||
Cluster: "cluster-1",
|
||||
AuthInfo: "admin",
|
||||
LocationOfOrigin: f.Name(),
|
||||
Extensions: map[string]runtime.Object{},
|
||||
},
|
||||
},
|
||||
Preferences: clientcmdapi.Preferences{
|
||||
Extensions: map[string]runtime.Object{},
|
||||
},
|
||||
Extensions: map[string]runtime.Object{},
|
||||
}
|
||||
|
||||
initialContent, err := clientcmd.Write(s.initialConfig)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
if _, err := f.Write(initialContent); err != nil {
|
||||
c.Fatalf("failed to write kubeconfig: %v", err)
|
||||
}
|
||||
|
||||
s.kubeconfigPath = f.Name()
|
||||
os.Setenv(teleport.EnvKubeConfig, s.kubeconfigPath)
|
||||
}
|
||||
|
||||
func (s *KubeconfigSuite) TearDownTest(c *check.C) {
|
||||
os.Remove(s.kubeconfigPath)
|
||||
}
|
||||
|
||||
func (s *KubeconfigSuite) TestLoadKubeConfig(c *check.C) {
|
||||
config, err := LoadKubeConfig()
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(*config, check.DeepEquals, s.initialConfig)
|
||||
}
|
||||
|
||||
func (s *KubeconfigSuite) TestSaveKubeConfig(c *check.C) {
|
||||
cfg := clientcmdapi.Config{
|
||||
CurrentContext: "a",
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"cluster": {
|
||||
CertificateAuthority: "fake-ca-file",
|
||||
Server: "https://1.2.3.4",
|
||||
LocationOfOrigin: s.kubeconfigPath,
|
||||
Extensions: map[string]runtime.Object{},
|
||||
},
|
||||
},
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"user": {
|
||||
LocationOfOrigin: s.kubeconfigPath,
|
||||
Extensions: map[string]runtime.Object{},
|
||||
},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"a": {
|
||||
Cluster: "cluster",
|
||||
AuthInfo: "user",
|
||||
LocationOfOrigin: s.kubeconfigPath,
|
||||
Extensions: map[string]runtime.Object{},
|
||||
},
|
||||
},
|
||||
Preferences: clientcmdapi.Preferences{
|
||||
Extensions: map[string]runtime.Object{},
|
||||
},
|
||||
Extensions: map[string]runtime.Object{},
|
||||
}
|
||||
|
||||
err := SaveKubeConfig(cfg)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
config, err := LoadKubeConfig()
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(*config, check.DeepEquals, cfg)
|
||||
}
|
||||
|
||||
func (s *KubeconfigSuite) TestUpdateKubeconfigWithValues(c *check.C) {
|
||||
const (
|
||||
clusterName = "teleport-cluster"
|
||||
clusterAddr = "https://1.2.3.6:3080"
|
||||
)
|
||||
creds, caCertPEM, err := s.genUserKey()
|
||||
c.Assert(err, check.IsNil)
|
||||
err = updateKubeconfigWithValues(clusterName, clusterAddr, creds)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
wantConfig := s.initialConfig
|
||||
wantConfig.Clusters[clusterName] = &clientcmdapi.Cluster{
|
||||
Server: clusterAddr,
|
||||
CertificateAuthorityData: caCertPEM,
|
||||
LocationOfOrigin: s.kubeconfigPath,
|
||||
Extensions: map[string]runtime.Object{},
|
||||
}
|
||||
wantConfig.AuthInfos[clusterName] = &clientcmdapi.AuthInfo{
|
||||
ClientCertificateData: creds.TLSCert,
|
||||
ClientKeyData: creds.Priv,
|
||||
LocationOfOrigin: s.kubeconfigPath,
|
||||
Extensions: map[string]runtime.Object{},
|
||||
}
|
||||
wantConfig.Contexts[clusterName] = &clientcmdapi.Context{
|
||||
Cluster: clusterName,
|
||||
AuthInfo: clusterName,
|
||||
LocationOfOrigin: s.kubeconfigPath,
|
||||
Extensions: map[string]runtime.Object{},
|
||||
}
|
||||
wantConfig.CurrentContext = clusterName
|
||||
|
||||
config, err := LoadKubeConfig()
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(*config, check.DeepEquals, wantConfig)
|
||||
}
|
||||
|
||||
func (s *KubeconfigSuite) genUserKey() (*client.Key, []byte, error) {
|
||||
caKey, caCert, err := tlsca.GenerateSelfSignedCA(pkix.Name{
|
||||
CommonName: "localhost",
|
||||
Organization: []string{"localhost"},
|
||||
}, nil, defaults.CATTL)
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
ca, err := tlsca.New(caCert, caKey)
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
keygen := testauthority.New()
|
||||
priv, pub, err := keygen.GenerateKeyPair("")
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
cryptoPub, err := sshutils.CryptoPublicKey(pub)
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
clock := clockwork.NewRealClock()
|
||||
tlsCert, err := ca.GenerateCertificate(tlsca.CertificateRequest{
|
||||
Clock: clock,
|
||||
PublicKey: cryptoPub,
|
||||
Subject: pkix.Name{
|
||||
CommonName: "teleport-user",
|
||||
},
|
||||
NotAfter: clock.Now().UTC().Add(time.Minute),
|
||||
})
|
||||
|
||||
return &client.Key{
|
||||
Priv: priv,
|
||||
Pub: pub,
|
||||
TLSCert: tlsCert,
|
||||
TrustedCA: []auth.TrustedCerts{{
|
||||
TLSCertificates: [][]byte{caCert},
|
||||
}},
|
||||
}, caCert, nil
|
||||
}
|
Loading…
Reference in a new issue