Skip to content

Commit

Permalink
feat: kubernetes config properties exposed as attributes
Browse files Browse the repository at this point in the history
The following `kube_config` related properties were exposed as attributes of the `ionoscloud_k8s_cluster` datasource:
- config - a detailed mapping of  `kube_config`
- user_tokens - a map of authentication tokens by user
- server - cluster server
- ca_crt - server ca certificate
  • Loading branch information
mflorin authored Jul 29, 2021
1 parent e06983d commit 602d872
Show file tree
Hide file tree
Showing 89 changed files with 766 additions and 5,094 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 5.2.6

- issue #66 - detailed kube config attributes implemented

## 5.2.5
- fix: fixes #1 - usage example updates
- documentation updates
Expand Down
88 changes: 86 additions & 2 deletions docs/data-sources/k8s_cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,92 @@ The following attributes are returned by the datasource:
* `available_upgrade_versions` - list of available versions for upgrading the cluster
* `viable_node_pool_versions` - list of versions that may be used for node pools under this cluster
* `node_pools` - list of the IDs of the node pools in this cluster
* `kube_config` - Kubernetes configuration
* `kube_config` - Raw Kubernetes configuration; use `yamlencode` or `jsonencode` when dumping this to a file
* `public` - the indicator if the cluster is public or private
* `gateway_ip` - the IP address of the gateway used by the cluster
* `api_subnet_allow_list` - access to the K8s API server is restricted to these CIDRs
* `s3_buckets` - list of S3 bucket configured for K8s usage
* `s3_buckets` - list of S3 bucket configured for K8s usage
* `config` - structured kubernetes config consisting of a list with 1 item with the following fields:
* api_version - Kubernetes API Version
* kind - "Config"
* current-context - string
* clusters - list of
* name - name of cluster
* cluster - map of
* certificate-authority-data - **base64 decoded** cluster CA data
* server - server address in the form `https://host:port`
* contexts - list of
* name - context name
* context - map of
* cluster - cluster name
* user - cluster user
* users - list of
* name - user name
* user - map of
* token - user token used for authentication
* `user_tokens` - a convenience map to search the token of a specific user
- key - is the user name
- value - is the token
* `server` - cluster server (same as `config[0].clusters[0].cluster.server` but provided as an attribute for ease of use)
* `ca_crt` - base64 decoded cluster certificate authority data (provided as an attribute for direct use)

**NOTE**: The whole `config` node is marked as **sensitive**.

## Example of accessing a kubernetes cluster using the user's token

```
resource "ionoscloud_k8s_cluster" "test" {
name = "test_cluster"
maintenance_window {
day_of_the_week = "Saturday"
time = "03:58:25Z"
}
}
data "ionoscloud_k8s_cluster" "test" {
name = "test_cluster"
}
provider "kubernetes" {
host = data.ionoscloud_k8s_cluster.test.server
token = data.ionoscloud_k8s_cluster.test.user_tokens["cluster-admin"]
}
```

## Example of accessing a kubernetes cluster using the token from the config

```
resource "ionoscloud_k8s_cluster" "test" {
name = "test_cluster"
maintenance_window {
day_of_the_week = "Saturday"
time = "03:58:25Z"
}
}
data "ionoscloud_k8s_cluster" "test" {
name = "test_cluster"
}
provider "kubernetes" {
host = data.ionoscloud_k8s_cluster.test.config[0].clusters[0].cluster.server
token = data.ionoscloud_k8s_cluster.test.config[0].users[0].user.token
}
```


## Example of dumping the kube_config raw data into a yaml file

**NOTE**: Dumping `kube_config` data into files poses a security risk.

```
data "ionoscloud_k8s_cluster" "k8s_cluster_example" {
name = "k8s-demo"
}
resource "null_resource" "getcfg" {
provisioner "local-exec" {
command = "echo \"${yamlencode(data.ionoscloud_k8s_cluster.k8s_cluster_example.kube_config)}\" > kubecfg.yaml"
}
}
```
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ go 1.14

require (
github.com/agext/levenshtein v1.2.3 // indirect
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 // indirect
github.com/aws/aws-sdk-go v1.37.0 // indirect
github.com/fatih/color v1.9.0 // indirect
github.com/go-test/deep v1.0.6 // indirect
Expand All @@ -26,5 +25,5 @@ require (
golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb // indirect
google.golang.org/api v0.34.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
)
90 changes: 38 additions & 52 deletions go.sum

Large diffs are not rendered by default.

230 changes: 230 additions & 0 deletions ionoscloud/data_source_k8s_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,41 @@ package ionoscloud

import (
"context"
"encoding/base64"
"errors"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
ionoscloud "github.com/ionos-cloud/sdk-go/v5"
"gopkg.in/yaml.v3"
)

type KubeConfig struct{
ApiVersion string `yaml:"apiVersion"`
Clusters []struct{
Name string
Cluster struct{
CaData string `yaml:"certificate-authority-data"`
Server string
}
}
Contexts []struct{
Name string
Context struct{
Cluster string
User string
}
}
CurrentContext string `yaml:"current-context"`
Kind string
Users []struct{
Name string
User struct{
Token string
}
}
// preferences - add it when its structure is clear
}

func dataSourceK8sCluster() *schema.Resource {
return &schema.Resource{
Read: dataSourceK8sReadCluster,
Expand Down Expand Up @@ -74,6 +103,113 @@ func dataSourceK8sCluster() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"config": {
Type: schema.TypeList,
Computed: true,
Sensitive: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"api_version": {
Type: schema.TypeString,
Computed: true,
Sensitive: true,

},
"current_context": {
Type: schema.TypeString,
Computed: true,
Sensitive: true,
},
"kind": {
Type: schema.TypeString,
Computed: true,
},
"users": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Computed: true,
Sensitive: true,

},
"user": {
Type: schema.TypeMap,
Computed: true,
Sensitive: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
"clusters": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Computed: true,
Sensitive: true,
},
"cluster": {
Type: schema.TypeMap,
Computed: true,
Sensitive: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
"contexts": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Computed: true,
Sensitive: true,
},
"context": {
Type: schema.TypeMap,
Computed: true,
Sensitive: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
},
},
},
"user_tokens": {
Type: schema.TypeMap,
Sensitive: true,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
Sensitive: true,
},
},
"ca_crt": {
Type: schema.TypeString,
Sensitive: true,
Computed: true,
},
"server": {
Type: schema.TypeString,
Sensitive: true,
Computed: true,
},
"public": {
Type: schema.TypeBool,
Description: "The indicator if the cluster is public or private. Be aware that setting it to false is " +
Expand Down Expand Up @@ -186,6 +322,96 @@ func dataSourceK8sReadCluster(d *schema.ResourceData, meta interface{}) error {
return nil
}

func setK8sConfigData(d *schema.ResourceData, configStr string) error {

var kubeConfig KubeConfig
if err := yaml.Unmarshal([]byte(configStr), &kubeConfig); err != nil {
return err
}

userTokens := map[string]string{}

var server string
var caCrt []byte

configMap := make(map[string]interface{})

configMap["api_version"] = kubeConfig.ApiVersion
configMap["current_context"] = kubeConfig.CurrentContext
configMap["kind"] = kubeConfig.Kind

clustersList := make([]map[string]interface{}, len(kubeConfig.Clusters))
for i, cluster := range kubeConfig.Clusters {

/* decode ca */
decodedCrt := make([]byte, base64.StdEncoding.DecodedLen(len(cluster.Cluster.CaData)))
if _, err := base64.StdEncoding.Decode(decodedCrt, []byte(cluster.Cluster.CaData)); err != nil {
return err
}

if len(caCrt) == 0 {
caCrt = decodedCrt
}

clustersList[i] = map[string]interface{}{
"name": cluster.Name,
"cluster": map[string]string{
"server": cluster.Cluster.Server,
"certificate_authority_data": string(decodedCrt),
},
}
}

configMap["clusters"] = clustersList

contextsList := make([]map[string]interface{}, len(kubeConfig.Contexts))
for i, contextVal := range kubeConfig.Contexts {
contextsList[i] = map[string]interface{}{
"name": contextVal.Name,
"context": map[string]string{
"cluster": contextVal.Context.Cluster,
"user": contextVal.Context.User,
},
}
}

configMap["contexts"] = contextsList

userList := make([]map[string]interface{}, len(kubeConfig.Users))
for i, user := range kubeConfig.Users {
userList[i] = map[string]interface{}{
"name": user.Name,
"user": map[string]interface{}{
"token": user.User.Token,
},
}

userTokens[user.Name] = user.User.Token
}

configMap["users"] = userList

configList := []map[string]interface{}{configMap}

if err := d.Set("config", configList); err != nil {
return err
}

if err := d.Set("user_tokens", userTokens); err != nil {
return err
}

if err := d.Set("server", server); err != nil {
return err
}

if err := d.Set("ca_crt", string(caCrt)); err != nil {
return err
}

return nil
}

func setK8sClusterData(d *schema.ResourceData, cluster *ionoscloud.KubernetesCluster, client *ionoscloud.APIClient) error {

if cluster.Id != nil {
Expand Down Expand Up @@ -304,6 +530,10 @@ func setK8sClusterData(d *schema.ResourceData, cluster *ionoscloud.KubernetesClu
if err := d.Set("kube_config", *kubeConfig.Properties.Kubeconfig); err != nil {
return err
}

if err := setK8sConfigData(d, *kubeConfig.Properties.Kubeconfig); err != nil {
return err
}
}

/* getting node pools */
Expand Down
Loading

0 comments on commit 602d872

Please sign in to comment.