2017-03-02 12:26:06 +01:00
|
|
|
package kubernetes
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
|
|
"github.com/mitchellh/go-homedir"
|
2017-05-26 08:50:26 +02:00
|
|
|
restclient "k8s.io/client-go/rest"
|
|
|
|
"k8s.io/client-go/tools/clientcmd"
|
|
|
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
|
|
|
kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
|
2017-03-02 12:26:06 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func Provider() terraform.ResourceProvider {
|
|
|
|
return &schema.Provider{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"host": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("KUBE_HOST", ""),
|
|
|
|
Description: "The hostname (in form of URI) of Kubernetes master.",
|
|
|
|
},
|
|
|
|
"username": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("KUBE_USER", ""),
|
|
|
|
Description: "The username to use for HTTP basic authentication when accessing the Kubernetes master endpoint.",
|
|
|
|
},
|
|
|
|
"password": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("KUBE_PASSWORD", ""),
|
|
|
|
Description: "The password to use for HTTP basic authentication when accessing the Kubernetes master endpoint.",
|
|
|
|
},
|
|
|
|
"insecure": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("KUBE_INSECURE", false),
|
|
|
|
Description: "Whether server should be accessed without verifying the TLS certificate.",
|
|
|
|
},
|
|
|
|
"client_certificate": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("KUBE_CLIENT_CERT_DATA", ""),
|
|
|
|
Description: "PEM-encoded client certificate for TLS authentication.",
|
|
|
|
},
|
|
|
|
"client_key": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("KUBE_CLIENT_KEY_DATA", ""),
|
|
|
|
Description: "PEM-encoded client certificate key for TLS authentication.",
|
|
|
|
},
|
|
|
|
"cluster_ca_certificate": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("KUBE_CLUSTER_CA_CERT_DATA", ""),
|
|
|
|
Description: "PEM-encoded root certificates bundle for TLS authentication.",
|
|
|
|
},
|
|
|
|
"config_path": {
|
2017-06-05 14:18:45 +02:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
DefaultFunc: schema.MultiEnvDefaultFunc(
|
|
|
|
[]string{
|
|
|
|
"KUBE_CONFIG",
|
|
|
|
"KUBECONFIG",
|
|
|
|
},
|
|
|
|
"~/.kube/config"),
|
2017-03-02 12:26:06 +01:00
|
|
|
Description: "Path to the kube config file, defaults to ~/.kube/config",
|
|
|
|
},
|
2017-04-10 17:13:09 +02:00
|
|
|
"config_context": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("KUBE_CTX", ""),
|
|
|
|
},
|
2017-03-02 12:26:06 +01:00
|
|
|
"config_context_auth_info": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("KUBE_CTX_AUTH_INFO", ""),
|
|
|
|
Description: "",
|
|
|
|
},
|
|
|
|
"config_context_cluster": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("KUBE_CTX_CLUSTER", ""),
|
|
|
|
Description: "",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
ResourcesMap: map[string]*schema.Resource{
|
2017-05-24 18:18:02 +02:00
|
|
|
"kubernetes_config_map": resourceKubernetesConfigMap(),
|
|
|
|
"kubernetes_horizontal_pod_autoscaler": resourceKubernetesHorizontalPodAutoscaler(),
|
|
|
|
"kubernetes_limit_range": resourceKubernetesLimitRange(),
|
|
|
|
"kubernetes_namespace": resourceKubernetesNamespace(),
|
|
|
|
"kubernetes_persistent_volume": resourceKubernetesPersistentVolume(),
|
|
|
|
"kubernetes_persistent_volume_claim": resourceKubernetesPersistentVolumeClaim(),
|
2017-06-08 15:58:10 +02:00
|
|
|
"kubernetes_pod": resourceKubernetesPod(),
|
2017-05-24 18:18:02 +02:00
|
|
|
"kubernetes_resource_quota": resourceKubernetesResourceQuota(),
|
|
|
|
"kubernetes_secret": resourceKubernetesSecret(),
|
|
|
|
"kubernetes_service": resourceKubernetesService(),
|
2017-03-02 12:26:06 +01:00
|
|
|
},
|
|
|
|
ConfigureFunc: providerConfigure,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
|
|
|
// Config file loading
|
|
|
|
cfg, err := tryLoadingConfigFile(d)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if cfg == nil {
|
|
|
|
cfg = &restclient.Config{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Overriding with static configuration
|
|
|
|
cfg.UserAgent = fmt.Sprintf("HashiCorp/1.0 Terraform/%s", terraform.VersionString())
|
|
|
|
|
|
|
|
if v, ok := d.GetOk("host"); ok {
|
|
|
|
cfg.Host = v.(string)
|
|
|
|
}
|
|
|
|
if v, ok := d.GetOk("username"); ok {
|
|
|
|
cfg.Username = v.(string)
|
|
|
|
}
|
|
|
|
if v, ok := d.GetOk("password"); ok {
|
|
|
|
cfg.Password = v.(string)
|
|
|
|
}
|
|
|
|
if v, ok := d.GetOk("insecure"); ok {
|
|
|
|
cfg.Insecure = v.(bool)
|
|
|
|
}
|
|
|
|
if v, ok := d.GetOk("cluster_ca_certificate"); ok {
|
|
|
|
cfg.CAData = bytes.NewBufferString(v.(string)).Bytes()
|
|
|
|
}
|
|
|
|
if v, ok := d.GetOk("client_certificate"); ok {
|
|
|
|
cfg.CertData = bytes.NewBufferString(v.(string)).Bytes()
|
|
|
|
}
|
|
|
|
if v, ok := d.GetOk("client_key"); ok {
|
|
|
|
cfg.KeyData = bytes.NewBufferString(v.(string)).Bytes()
|
|
|
|
}
|
|
|
|
|
|
|
|
k, err := kubernetes.NewForConfig(cfg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Failed to configure: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return k, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func tryLoadingConfigFile(d *schema.ResourceData) (*restclient.Config, error) {
|
|
|
|
path, err := homedir.Expand(d.Get("config_path").(string))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
loader := &clientcmd.ClientConfigLoadingRules{
|
|
|
|
ExplicitPath: path,
|
|
|
|
}
|
2017-04-10 17:13:09 +02:00
|
|
|
|
2017-03-02 12:26:06 +01:00
|
|
|
overrides := &clientcmd.ConfigOverrides{}
|
2017-04-10 17:13:09 +02:00
|
|
|
ctxSuffix := "; default context"
|
|
|
|
|
|
|
|
ctx, ctxOk := d.GetOk("config_context")
|
2017-03-02 12:26:06 +01:00
|
|
|
authInfo, authInfoOk := d.GetOk("config_context_auth_info")
|
|
|
|
cluster, clusterOk := d.GetOk("config_context_cluster")
|
2017-04-10 17:13:09 +02:00
|
|
|
if ctxOk || authInfoOk || clusterOk {
|
|
|
|
ctxSuffix = "; overriden context"
|
|
|
|
if ctxOk {
|
|
|
|
overrides.CurrentContext = ctx.(string)
|
|
|
|
ctxSuffix += fmt.Sprintf("; config ctx: %s", overrides.CurrentContext)
|
|
|
|
log.Printf("[DEBUG] Using custom current context: %q", overrides.CurrentContext)
|
|
|
|
}
|
|
|
|
|
2017-03-02 12:26:06 +01:00
|
|
|
overrides.Context = clientcmdapi.Context{}
|
|
|
|
if authInfoOk {
|
|
|
|
overrides.Context.AuthInfo = authInfo.(string)
|
2017-04-10 17:13:09 +02:00
|
|
|
ctxSuffix += fmt.Sprintf("; auth_info: %s", overrides.Context.AuthInfo)
|
2017-03-02 12:26:06 +01:00
|
|
|
}
|
|
|
|
if clusterOk {
|
|
|
|
overrides.Context.Cluster = cluster.(string)
|
2017-04-10 17:13:09 +02:00
|
|
|
ctxSuffix += fmt.Sprintf("; cluster: %s", overrides.Context.Cluster)
|
2017-03-02 12:26:06 +01:00
|
|
|
}
|
2017-04-10 17:13:09 +02:00
|
|
|
log.Printf("[DEBUG] Using overidden context: %#v", overrides.Context)
|
2017-03-02 12:26:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cc := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loader, overrides)
|
|
|
|
cfg, err := cc.ClientConfig()
|
|
|
|
if err != nil {
|
|
|
|
if pathErr, ok := err.(*os.PathError); ok && os.IsNotExist(pathErr.Err) {
|
|
|
|
log.Printf("[INFO] Unable to load config file as it doesn't exist at %q", path)
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("Failed to load config (%s%s): %s", path, ctxSuffix, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[INFO] Successfully loaded config file (%s%s)", path, ctxSuffix)
|
|
|
|
return cfg, nil
|
|
|
|
}
|