Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for outputting kube config credentials from AKS #953

Merged
merged 14 commits into from
Apr 26, 2018
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
74 changes: 74 additions & 0 deletions azurerm/helpers/kubernetes/kube_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package kubernetes

import (
"fmt"

yaml "gopkg.in/yaml.v2"
)

type clusterItem struct {
Name string `yaml:"name"`
Cluster cluster `yaml:"cluster"`
}

type cluster struct {
ClusterAuthorityData string `yaml:"certificate-authority-data"`
Server string `yaml:"server"`
}

type userItem struct {
Name string `yaml:"name"`
User user `yaml:"user"`
}

type user struct {
ClientCertificteData string `yaml:"client-certificate-data"`
Token string `yaml:"token"`
ClientKeyData string `yaml:"client-key-data"`
}

type contextItem struct {
Name string `yaml:"name"`
Context context `yaml:"context"`
}

type context struct {
Cluster string `yaml:"cluster"`
User string `yaml:"user"`
Namespace string `yaml:"namespace,omitempty"`
}

type KubeConfig struct {
APIVersion string `yaml:"apiVersion"`
Clusters []clusterItem `yaml:"clusters"`
Users []userItem `yaml:"users"`
Contexts []contextItem `yaml:"contexts,omitempty"`
CurrentContext string `yaml:"current-context,omitempty"`
Kind string `yaml:"kind,omitempty"`
Preferences map[string]interface{} `yaml:"preferences,omitempty"`
}

func ParseKubeConfig(config string) (*KubeConfig, error) {
if config == "" {
return nil, fmt.Errorf("Cannot parse empty config")
}

var kubeConfig KubeConfig
err := yaml.Unmarshal([]byte(config), &kubeConfig)
if err != nil {
return nil, fmt.Errorf("Failed to unmarshal YAML config with error %+v", err)
}
if len(kubeConfig.Clusters) <= 0 || len(kubeConfig.Users) <= 0 {
return nil, fmt.Errorf("Config %+v contains no valid clusters or users", kubeConfig)
}
user := kubeConfig.Users[0].User
if user.Token == "" && (user.ClientCertificteData == "" || user.ClientKeyData == "") {
return nil, fmt.Errorf("Config requires either token or certificate auth for user %+v", user)
}
cluster := kubeConfig.Clusters[0].Cluster
if cluster.Server == "" {
return nil, fmt.Errorf("Config has invalid or non existent server for cluster %+v", cluster)
}

return &kubeConfig, nil
}
189 changes: 189 additions & 0 deletions azurerm/helpers/kubernetes/kube_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package kubernetes

import (
"fmt"
"io/ioutil"
"path/filepath"
"reflect"
"testing"
)

func TestParseKubeConfig(t *testing.T) {
testCases := []struct {
sourceFile string
expected KubeConfig
checkFunc func(expected KubeConfig, config string) (bool, error)
}{
{
"user_with_token.yml",
KubeConfig{
APIVersion: "v1",
Clusters: []clusterItem{
{
Name: "test-cluster",
Cluster: cluster{
Server: "https://testcluster.net:8080",
},
},
},
Users: []userItem{
{
Name: "test-user",
User: user{
Token: "test-token",
},
},
},
Kind: "Config",
},
isValidConfig,
},
{
"user_with_cert.yml",
KubeConfig{
APIVersion: "v1",
Clusters: []clusterItem{
{
Name: "test-cluster",
Cluster: cluster{
ClusterAuthorityData: "test-cluster-authority-data",
Server: "https://testcluster.org:443",
},
},
},
Users: []userItem{
{
Name: "test-user",
User: user{
ClientCertificteData: "test-client-certificate-data",
ClientKeyData: "test-client-key-data",
},
},
},
Contexts: []contextItem{
{
Name: "test-cluster",
Context: context{
Cluster: "test-cluster",
User: "test-user",
Namespace: "test-namespace",
},
},
},
CurrentContext: "test-cluster",
Kind: "Config",
Preferences: nil,
},
isValidConfig,
},
{
"user_with_cert_token.yml",
KubeConfig{
APIVersion: "v1",
Clusters: []clusterItem{
{
Name: "test-cluster",
Cluster: cluster{
ClusterAuthorityData: "test-cluster-authority-data",
Server: "https://testcluster.org:443",
},
},
},
Users: []userItem{
{
Name: "test-user",
User: user{
ClientCertificteData: "test-client-certificate-data",
ClientKeyData: "test-client-key-data",
Token: "test-token",
},
},
},
Contexts: []contextItem{
{
Name: "test-cluster",
Context: context{
Cluster: "test-cluster",
User: "test-user",
Namespace: "test-namespace",
},
},
},
CurrentContext: "test-cluster",
Kind: "Config",
Preferences: map[string]interface{}{
"colors": true,
},
},
isValidConfig,
},
{
"user_with_no_auth.yml",
KubeConfig{},
isInvalidConfig,
},
{
"no_cluster.yml",
KubeConfig{},
isInvalidConfig,
},
{
"no_user.yml",
KubeConfig{},
isInvalidConfig,
},
{
"user_with_partial_auth.yml",
KubeConfig{},
isInvalidConfig,
},
{
"cluster_with_no_server.yml",
KubeConfig{},
isInvalidConfig,
},
}

for i, test := range testCases {
encodedConfig := LoadConfig(test.sourceFile)
if len(encodedConfig) <= 0 {
t.Fatalf("Test case [%d]: Failed to read config from file '%+v' \n",
i, test.sourceFile)
}
if success, err := test.checkFunc(test.expected, encodedConfig); !success {
t.Fatalf("Test case [%d]: Failed, config '%+v' with error: '%+v'",
i, test.sourceFile, err)
}
}
}

func isValidConfig(expected KubeConfig, encodedConfig string) (bool, error) {
result, err := ParseKubeConfig(encodedConfig)
if err != nil {
return false, err
}

if !reflect.DeepEqual(expected, *result) {
return false, fmt.Errorf("expected '%+v but got '%+v' with encoded config '%+v'",
expected, *result, encodedConfig)
}
return true, nil
}

func isInvalidConfig(expected KubeConfig, encodedConfig string) (bool, error) {
_, err := ParseKubeConfig(encodedConfig)
if err == nil {
return false, fmt.Errorf("expected test to throw error but didn't")
}
return true, nil
}

func LoadConfig(fileName string) string {
filePath := filepath.Join("testdata", fileName)
bytes, err := ioutil.ReadFile(filePath)
if err != nil {
return ""
}

return string(bytes)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: v1
clusters:
- cluster:
name: test-cluster
users:
- name: test-user
user:
token: test-token
kind: Config
7 changes: 7 additions & 0 deletions azurerm/helpers/kubernetes/testdata/no_cluster.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
clusters:
users:
- name: test-user
user:
token: test-token
kind: Config
7 changes: 7 additions & 0 deletions azurerm/helpers/kubernetes/testdata/no_user.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: test-cluster-authority-data
server: https://testcluster.org:443
name: test-cluster
users:
19 changes: 19 additions & 0 deletions azurerm/helpers/kubernetes/testdata/user_with_cert.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: test-cluster-authority-data
server: https://testcluster.org:443
name: test-cluster
contexts:
- context:
cluster: test-cluster
user: test-user
namespace: test-namespace
name: test-cluster
current-context: test-cluster
users:
- name: test-user
user:
client-certificate-data: test-client-certificate-data
client-key-data: test-client-key-data
kind: Config
22 changes: 22 additions & 0 deletions azurerm/helpers/kubernetes/testdata/user_with_cert_token.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: test-cluster-authority-data
server: https://testcluster.org:443
name: test-cluster
contexts:
- context:
cluster: test-cluster
user: test-user
namespace: test-namespace
name: test-cluster
current-context: test-cluster
users:
- name: test-user
user:
client-certificate-data: test-client-certificate-data
client-key-data: test-client-key-data
token: test-token
kind: Config
preferences:
colors: true
9 changes: 9 additions & 0 deletions azurerm/helpers/kubernetes/testdata/user_with_no_auth.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: test-cluster-authority-data
server: https://testcluster.org:443
name: test-cluster
users:
- name: test-user
user:
20 changes: 20 additions & 0 deletions azurerm/helpers/kubernetes/testdata/user_with_partial_auth.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: test-cluster-authority-data
server: https://testcluster.org:443
name: test-cluster
contexts:
- context:
cluster: test-cluster
user: test-user
namespace: test-namespace
name: test-cluster
current-context: test-cluster
users:
- name: test-user
user:
client-certificate-data: test-client-certificate-data
kind: Config
preferences:
colors: true
10 changes: 10 additions & 0 deletions azurerm/helpers/kubernetes/testdata/user_with_token.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: v1
clusters:
- cluster:
server: https://testcluster.net:8080
name: test-cluster
users:
- name: test-user
user:
token: test-token
kind: Config
Loading