doks: fetch new kubeconfig credentials only when expired (#311)
* Upgrade godo to v1.22.0 * doks: fetch new kubeconfig credentials only when expired
This commit is contained in:
parent
356fd5c157
commit
0b606c4e68
|
@ -2,6 +2,7 @@ package digitalocean
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
@ -134,6 +135,11 @@ func kubernetesConfigSchema() *schema.Schema {
|
|||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"expires_at": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -220,14 +226,28 @@ func digitaloceanKubernetesClusterRead(client *godo.Client, cluster *godo.Kubern
|
|||
}
|
||||
}
|
||||
|
||||
// fetch the K8s config and update the resource
|
||||
config, resp, err := client.Kubernetes.GetKubeConfig(context.Background(), cluster.ID)
|
||||
if err != nil {
|
||||
if resp != nil && resp.StatusCode == 404 {
|
||||
return fmt.Errorf("Unable to fetch Kubernetes config: %s", err)
|
||||
// fetch cluster credentials and update the resource if the credentials are expired.
|
||||
var creds map[string]interface{}
|
||||
if d.Get("kube_config") != nil && len(d.Get("kube_config").([]interface{})) > 0 {
|
||||
creds = d.Get("kube_config").([]interface{})[0].(map[string]interface{})
|
||||
}
|
||||
var expiresAt time.Time
|
||||
if creds["expires_at"] != nil && creds["expires_at"].(string) != "" {
|
||||
var err error
|
||||
expiresAt, err = time.Parse(time.RFC3339, creds["expires_at"].(string))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to parse Kubernetes credentials expiry: %s", err)
|
||||
}
|
||||
}
|
||||
d.Set("kube_config", flattenKubeConfig(config))
|
||||
if expiresAt.IsZero() || expiresAt.Before(time.Now()) {
|
||||
creds, resp, err := client.Kubernetes.GetCredentials(context.Background(), cluster.ID, &godo.KubernetesClusterCredentialsGetRequest{})
|
||||
if err != nil {
|
||||
if resp != nil && resp.StatusCode == 404 {
|
||||
return fmt.Errorf("Unable to fetch Kubernetes credentials: %s", err)
|
||||
}
|
||||
}
|
||||
d.Set("kube_config", flattenCredentials(cluster.Name, creds))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -328,8 +348,8 @@ type kubernetesConfigCluster struct {
|
|||
Name string `yaml:"name"`
|
||||
}
|
||||
type kubernetesConfigClusterData struct {
|
||||
ClusterCACertificate string `yaml:"certificate-authority-data"`
|
||||
Server string `yaml:"server"`
|
||||
CertificateAuthorityData string `yaml:"certificate-authority-data"`
|
||||
Server string `yaml:"server"`
|
||||
}
|
||||
|
||||
type kubernetesConfigUser struct {
|
||||
|
@ -338,40 +358,60 @@ type kubernetesConfigUser struct {
|
|||
}
|
||||
|
||||
type kubernetesConfigUserData struct {
|
||||
ClientKeyData string `yaml:"client-key-data"`
|
||||
ClientCertificate string `yaml:"client-certificate-data"`
|
||||
Token string `yaml:"token"`
|
||||
ClientKeyData string `yaml:"client-key-data"`
|
||||
ClientCertificateData string `yaml:"client-certificate-data"`
|
||||
Token string `yaml:"token"`
|
||||
}
|
||||
|
||||
func flattenKubeConfig(config *godo.KubernetesClusterConfig) []interface{} {
|
||||
rawConfig := map[string]interface{}{
|
||||
"raw_config": string(config.KubeconfigYAML),
|
||||
func flattenCredentials(name string, creds *godo.KubernetesClusterCredentials) []interface{} {
|
||||
raw := map[string]interface{}{
|
||||
"cluster_ca_certificate": base64.StdEncoding.EncodeToString(creds.CertificateAuthorityData),
|
||||
"host": creds.Server,
|
||||
"token": creds.Token,
|
||||
"expires_at": creds.ExpiresAt.Format(time.RFC3339),
|
||||
}
|
||||
|
||||
// parse the yaml into an object
|
||||
var c kubernetesConfig
|
||||
err := yaml.Unmarshal(config.KubeconfigYAML, &c)
|
||||
if creds.ClientKeyData != nil {
|
||||
raw["client_key"] = string(creds.ClientKeyData)
|
||||
}
|
||||
|
||||
if creds.ClientCertificateData != nil {
|
||||
raw["client_certificate"] = string(creds.ClientCertificateData)
|
||||
}
|
||||
|
||||
kubeconfigYAML, err := renderKubeconfig(name, creds)
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] error unmarshalling config: %s", err)
|
||||
log.Printf("[DEBUG] error marshalling config: %s", err)
|
||||
return nil
|
||||
}
|
||||
raw["raw_config"] = string(kubeconfigYAML)
|
||||
|
||||
if len(c.Clusters) < 1 {
|
||||
return []interface{}{rawConfig}
|
||||
return []interface{}{raw}
|
||||
}
|
||||
|
||||
func renderKubeconfig(name string, creds *godo.KubernetesClusterCredentials) ([]byte, error) {
|
||||
config := kubernetesConfig{
|
||||
Clusters: []kubernetesConfigCluster{{
|
||||
Name: "",
|
||||
Cluster: kubernetesConfigClusterData{
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString(creds.CertificateAuthorityData),
|
||||
Server: creds.Server,
|
||||
},
|
||||
}},
|
||||
Users: []kubernetesConfigUser{{
|
||||
Name: "",
|
||||
User: kubernetesConfigUserData{
|
||||
Token: creds.Token,
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
rawConfig["cluster_ca_certificate"] = c.Clusters[0].Cluster.ClusterCACertificate
|
||||
rawConfig["host"] = c.Clusters[0].Cluster.Server
|
||||
|
||||
if len(c.Users) < 1 {
|
||||
return []interface{}{rawConfig}
|
||||
if creds.ClientKeyData != nil {
|
||||
config.Users[0].User.ClientKeyData = base64.StdEncoding.EncodeToString(creds.ClientKeyData)
|
||||
}
|
||||
|
||||
rawConfig["client_key"] = c.Users[0].User.ClientKeyData
|
||||
rawConfig["client_certificate"] = c.Users[0].User.ClientCertificate
|
||||
rawConfig["token"] = c.Users[0].User.Token
|
||||
|
||||
return []interface{}{rawConfig}
|
||||
if creds.ClientCertificateData != nil {
|
||||
config.Users[0].User.ClientCertificateData = base64.StdEncoding.EncodeToString(creds.ClientCertificateData)
|
||||
}
|
||||
return yaml.Marshal(config)
|
||||
}
|
||||
|
||||
// we need to filter tags to remove any automatically added to avoid state problems,
|
||||
|
|
|
@ -53,6 +53,7 @@ func TestAccDigitalOceanKubernetesCluster_Basic(t *testing.T) {
|
|||
resource.TestCheckResourceAttrSet("digitalocean_kubernetes_cluster.foobar", "kube_config.0.cluster_ca_certificate"),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_kubernetes_cluster.foobar", "kube_config.0.host"),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_kubernetes_cluster.foobar", "kube_config.0.token"),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_kubernetes_cluster.foobar", "kube_config.0.expires_at"),
|
||||
),
|
||||
},
|
||||
},
|
||||
|
|
2
go.mod
2
go.mod
|
@ -3,7 +3,7 @@ module github.com/terraform-providers/terraform-provider-digitalocean
|
|||
require (
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.6.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.22.0
|
||||
github.com/digitalocean/godo v1.21.0
|
||||
github.com/digitalocean/godo v1.22.0
|
||||
github.com/hashicorp/terraform v0.12.8
|
||||
github.com/terraform-providers/terraform-provider-kubernetes v1.7.0
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
||||
|
|
4
go.sum
4
go.sum
|
@ -107,8 +107,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/digitalocean/godo v1.21.0 h1:oSgoZGague6FDPSRjhCiYd8s66QjkWJn+Jt4R/1kEyo=
|
||||
github.com/digitalocean/godo v1.21.0/go.mod h1:AAPQ+tiM4st79QHlEBTg8LM7JQNre4SAQCbn56wEyKY=
|
||||
github.com/digitalocean/godo v1.22.0 h1:bVFBKXW2TlynZ9SqmlM6ZSW6UPEzFckltSIUT5NC8L4=
|
||||
github.com/digitalocean/godo v1.22.0/go.mod h1:iJnN9rVu6K5LioLxLimlq0uRI+y/eAQjROUmeU/r0hY=
|
||||
github.com/dimchansky/utfbom v1.0.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
|
|
|
@ -7,6 +7,7 @@ go:
|
|||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
# Change Log
|
||||
|
||||
## [v1.22.0] - 2019-09-24
|
||||
|
||||
- #259 Add Kubernetes GetCredentials method - @snormore
|
||||
|
||||
## [v1.21.1] - 2019-09-19
|
||||
|
||||
- #257 Upgrade to Go 1.13 - @bentranter
|
||||
|
||||
## [v1.21.0] - 2019-09-16
|
||||
|
||||
- #255 Add DropletID to Kubernetes Node instance - @snormore
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module github.com/digitalocean/godo
|
||||
|
||||
go 1.12
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/google/go-querystring v1.0.0
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
|
@ -14,6 +15,8 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI
|
|||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
libraryVersion = "1.21.0"
|
||||
libraryVersion = "1.22.0"
|
||||
defaultBaseURL = "https://api.digitalocean.com/"
|
||||
userAgent = "godo/" + libraryVersion
|
||||
mediaType = "application/json"
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
@ -27,6 +28,7 @@ type KubernetesService interface {
|
|||
GetUser(context.Context, string) (*KubernetesClusterUser, *Response, error)
|
||||
GetUpgrades(context.Context, string) ([]*KubernetesVersion, *Response, error)
|
||||
GetKubeConfig(context.Context, string) (*KubernetesClusterConfig, *Response, error)
|
||||
GetCredentials(context.Context, string, *KubernetesClusterCredentialsGetRequest) (*KubernetesClusterCredentials, *Response, error)
|
||||
List(context.Context, *ListOptions) ([]*KubernetesCluster, *Response, error)
|
||||
Update(context.Context, string, *KubernetesClusterUpdateRequest) (*KubernetesCluster, *Response, error)
|
||||
Upgrade(context.Context, string, *KubernetesClusterUpgradeRequest) (*Response, error)
|
||||
|
@ -117,6 +119,11 @@ type KubernetesNodeDeleteRequest struct {
|
|||
SkipDrain bool `json:"skip_drain,omitempty"`
|
||||
}
|
||||
|
||||
// KubernetesClusterCredentialsGetRequest is a request to get cluster credentials.
|
||||
type KubernetesClusterCredentialsGetRequest struct {
|
||||
ExpirySeconds *int `json:"expiry_seconds,omitempty"`
|
||||
}
|
||||
|
||||
// KubernetesCluster represents a Kubernetes cluster.
|
||||
type KubernetesCluster struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
|
@ -146,6 +153,16 @@ type KubernetesClusterUser struct {
|
|||
Groups []string `json:"groups,omitempty"`
|
||||
}
|
||||
|
||||
// KubernetesClusterCredentials represents Kubernetes cluster credentials.
|
||||
type KubernetesClusterCredentials struct {
|
||||
Server string `json:"server"`
|
||||
CertificateAuthorityData []byte `json:"certificate_authority_data"`
|
||||
ClientCertificateData []byte `json:"client_certificate_data"`
|
||||
ClientKeyData []byte `json:"client_key_data"`
|
||||
Token string `json:"token"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
}
|
||||
|
||||
// KubernetesMaintenancePolicy is a configuration to set the maintenance window
|
||||
// of a cluster
|
||||
type KubernetesMaintenancePolicy struct {
|
||||
|
@ -480,6 +497,25 @@ func (svc *KubernetesServiceOp) GetKubeConfig(ctx context.Context, clusterID str
|
|||
return res, resp, nil
|
||||
}
|
||||
|
||||
// GetCredentials returns a Kubernetes API server credentials for the specified cluster.
|
||||
func (svc *KubernetesServiceOp) GetCredentials(ctx context.Context, clusterID string, get *KubernetesClusterCredentialsGetRequest) (*KubernetesClusterCredentials, *Response, error) {
|
||||
path := fmt.Sprintf("%s/%s/credentials", kubernetesClustersPath, clusterID)
|
||||
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
q := req.URL.Query()
|
||||
if get.ExpirySeconds != nil {
|
||||
q.Add("expiry_seconds", strconv.Itoa(*get.ExpirySeconds))
|
||||
}
|
||||
credentials := new(KubernetesClusterCredentials)
|
||||
resp, err := svc.client.Do(ctx, req, credentials)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return credentials, resp, nil
|
||||
}
|
||||
|
||||
// Update updates a Kubernetes cluster's properties.
|
||||
func (svc *KubernetesServiceOp) Update(ctx context.Context, clusterID string, update *KubernetesClusterUpdateRequest) (*KubernetesCluster, *Response, error) {
|
||||
path := fmt.Sprintf("%s/%s", kubernetesClustersPath, clusterID)
|
||||
|
|
|
@ -79,7 +79,7 @@ github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1
|
|||
github.com/davecgh/go-spew/spew
|
||||
# github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/dgrijalva/jwt-go
|
||||
# github.com/digitalocean/godo v1.21.0
|
||||
# github.com/digitalocean/godo v1.22.0
|
||||
github.com/digitalocean/godo
|
||||
# github.com/fatih/color v1.7.0
|
||||
github.com/fatih/color
|
||||
|
|
Loading…
Reference in New Issue