Merge pull request #12372 from hashicorp/f-kubernetes
kubernetes: Add provider + namespace resource
This commit is contained in:
commit
4448e45678
|
@ -0,0 +1,171 @@
|
||||||
|
package kubernetes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
|
||||||
|
"k8s.io/kubernetes/pkg/client/restclient"
|
||||||
|
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||||
|
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
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": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("KUBE_CONFIG", "~/.kube/config"),
|
||||||
|
Description: "Path to the kube config file, defaults to ~/.kube/config",
|
||||||
|
},
|
||||||
|
"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{
|
||||||
|
"kubernetes_namespace": resourceKubernetesNamespace(),
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
overrides := &clientcmd.ConfigOverrides{}
|
||||||
|
ctxSuffix := "; no context"
|
||||||
|
authInfo, authInfoOk := d.GetOk("config_context_auth_info")
|
||||||
|
cluster, clusterOk := d.GetOk("config_context_cluster")
|
||||||
|
if authInfoOk || clusterOk {
|
||||||
|
overrides.Context = clientcmdapi.Context{}
|
||||||
|
if authInfoOk {
|
||||||
|
overrides.Context.AuthInfo = authInfo.(string)
|
||||||
|
}
|
||||||
|
if clusterOk {
|
||||||
|
overrides.Context.Cluster = cluster.(string)
|
||||||
|
}
|
||||||
|
ctxSuffix = fmt.Sprintf("; auth_info: %s, cluster: %s",
|
||||||
|
overrides.Context.AuthInfo, overrides.Context.Cluster)
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] Using override context: %#v", *overrides)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package kubernetes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testAccProviders map[string]terraform.ResourceProvider
|
||||||
|
var testAccProvider *schema.Provider
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
testAccProvider = Provider().(*schema.Provider)
|
||||||
|
testAccProviders = map[string]terraform.ResourceProvider{
|
||||||
|
"kubernetes": testAccProvider,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProvider(t *testing.T) {
|
||||||
|
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProvider_impl(t *testing.T) {
|
||||||
|
var _ terraform.ResourceProvider = Provider()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccPreCheck(t *testing.T) {
|
||||||
|
hasFileCfg := (os.Getenv("KUBE_CTX_AUTH_INFO") != "" && os.Getenv("KUBE_CTX_CLUSTER") != "")
|
||||||
|
hasStaticCfg := (os.Getenv("KUBE_HOST") != "" &&
|
||||||
|
os.Getenv("KUBE_USER") != "" &&
|
||||||
|
os.Getenv("KUBE_PASSWORD") != "" &&
|
||||||
|
os.Getenv("KUBE_CLIENT_CERT_DATA") != "" &&
|
||||||
|
os.Getenv("KUBE_CLIENT_KEY_DATA") != "" &&
|
||||||
|
os.Getenv("KUBE_CLUSTER_CA_CERT_DATA") != "")
|
||||||
|
|
||||||
|
if !hasFileCfg && !hasStaticCfg {
|
||||||
|
t.Fatalf("File config (KUBE_CTX_AUTH_INFO and KUBE_CTX_CLUSTER) or static configuration"+
|
||||||
|
" (%s) must be set for acceptance tests",
|
||||||
|
strings.Join([]string{
|
||||||
|
"KUBE_HOST",
|
||||||
|
"KUBE_USER",
|
||||||
|
"KUBE_PASSWORD",
|
||||||
|
"KUBE_CLIENT_CERT_DATA",
|
||||||
|
"KUBE_CLIENT_KEY_DATA",
|
||||||
|
"KUBE_CLUSTER_CA_CERT_DATA",
|
||||||
|
}, ", "))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
package kubernetes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"k8s.io/kubernetes/pkg/api/errors"
|
||||||
|
api "k8s.io/kubernetes/pkg/api/v1"
|
||||||
|
kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceKubernetesNamespace() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceKubernetesNamespaceCreate,
|
||||||
|
Read: resourceKubernetesNamespaceRead,
|
||||||
|
Exists: resourceKubernetesNamespaceExists,
|
||||||
|
Update: resourceKubernetesNamespaceUpdate,
|
||||||
|
Delete: resourceKubernetesNamespaceDelete,
|
||||||
|
Importer: &schema.ResourceImporter{
|
||||||
|
State: schema.ImportStatePassthrough,
|
||||||
|
},
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"metadata": metadataSchema("namespace"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceKubernetesNamespaceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*kubernetes.Clientset)
|
||||||
|
|
||||||
|
metadata := expandMetadata(d.Get("metadata").([]interface{}))
|
||||||
|
namespace := api.Namespace{
|
||||||
|
ObjectMeta: metadata,
|
||||||
|
}
|
||||||
|
log.Printf("[INFO] Creating new namespace: %#v", namespace)
|
||||||
|
out, err := conn.CoreV1().Namespaces().Create(&namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("[INFO] Submitted new namespace: %#v", out)
|
||||||
|
d.SetId(out.Name)
|
||||||
|
|
||||||
|
return resourceKubernetesNamespaceRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceKubernetesNamespaceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*kubernetes.Clientset)
|
||||||
|
|
||||||
|
name := d.Id()
|
||||||
|
log.Printf("[INFO] Reading namespace %s", name)
|
||||||
|
namespace, err := conn.CoreV1().Namespaces().Get(name)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[DEBUG] Received error: %#v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("[INFO] Received namespace: %#v", namespace)
|
||||||
|
err = d.Set("metadata", flattenMetadata(namespace.ObjectMeta))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceKubernetesNamespaceUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*kubernetes.Clientset)
|
||||||
|
|
||||||
|
metadata := expandMetadata(d.Get("metadata").([]interface{}))
|
||||||
|
// This is necessary in case the name is generated
|
||||||
|
metadata.Name = d.Id()
|
||||||
|
|
||||||
|
namespace := api.Namespace{
|
||||||
|
ObjectMeta: metadata,
|
||||||
|
}
|
||||||
|
log.Printf("[INFO] Updating namespace: %#v", namespace)
|
||||||
|
out, err := conn.CoreV1().Namespaces().Update(&namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("[INFO] Submitted updated namespace: %#v", out)
|
||||||
|
d.SetId(out.Name)
|
||||||
|
|
||||||
|
return resourceKubernetesNamespaceRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceKubernetesNamespaceDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*kubernetes.Clientset)
|
||||||
|
|
||||||
|
name := d.Id()
|
||||||
|
log.Printf("[INFO] Deleting namespace: %#v", name)
|
||||||
|
err := conn.CoreV1().Namespaces().Delete(name, &api.DeleteOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Target: []string{},
|
||||||
|
Pending: []string{"Terminating"},
|
||||||
|
Timeout: 5 * time.Minute,
|
||||||
|
Refresh: func() (interface{}, string, error) {
|
||||||
|
out, err := conn.CoreV1().Namespaces().Get(name)
|
||||||
|
if err != nil {
|
||||||
|
if statusErr, ok := err.(*errors.StatusError); ok && statusErr.ErrStatus.Code == 404 {
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
log.Printf("[ERROR] Received error: %#v", err)
|
||||||
|
return out, "Error", err
|
||||||
|
}
|
||||||
|
|
||||||
|
statusPhase := fmt.Sprintf("%v", out.Status.Phase)
|
||||||
|
log.Printf("[DEBUG] Namespace %s status received: %#v", out.Name, statusPhase)
|
||||||
|
return out, statusPhase, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err = stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("[INFO] Namespace %s deleted", name)
|
||||||
|
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceKubernetesNamespaceExists(d *schema.ResourceData, meta interface{}) (bool, error) {
|
||||||
|
conn := meta.(*kubernetes.Clientset)
|
||||||
|
|
||||||
|
name := d.Id()
|
||||||
|
log.Printf("[INFO] Checking namespace %s", name)
|
||||||
|
_, err := conn.CoreV1().Namespaces().Get(name)
|
||||||
|
if err != nil {
|
||||||
|
if statusErr, ok := err.(*errors.StatusError); ok && statusErr.ErrStatus.Code == 404 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] Received error: %#v", err)
|
||||||
|
}
|
||||||
|
log.Printf("[INFO] Namespace %s exists", name)
|
||||||
|
return true, err
|
||||||
|
}
|
|
@ -0,0 +1,272 @@
|
||||||
|
package kubernetes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
api "k8s.io/kubernetes/pkg/api/v1"
|
||||||
|
kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccKubernetesNamespace_basic(t *testing.T) {
|
||||||
|
var conf api.Namespace
|
||||||
|
nsName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
IDRefreshName: "kubernetes_namespace.test",
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckKubernetesNamespaceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccKubernetesNamespaceConfig_basic(nsName),
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
testAccCheckKubernetesNamespaceExists("kubernetes_namespace.test", &conf),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.annotations.%", "2"),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.annotations.TestAnnotationOne", "one"),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.annotations.TestAnnotationTwo", "two"),
|
||||||
|
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{"TestAnnotationOne": "one", "TestAnnotationTwo": "two"}),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.labels.%", "3"),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.labels.TestLabelOne", "one"),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.labels.TestLabelTwo", "two"),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.labels.TestLabelThree", "three"),
|
||||||
|
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{"TestLabelOne": "one", "TestLabelTwo": "two", "TestLabelThree": "three"}),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.name", nsName),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.generation"),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.resource_version"),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.self_link"),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.uid"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Config: testAccKubernetesNamespaceConfig_smallerLists(nsName),
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
testAccCheckKubernetesNamespaceExists("kubernetes_namespace.test", &conf),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.annotations.%", "2"),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.annotations.TestAnnotationOne", "one"),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.annotations.Different", "1234"),
|
||||||
|
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{"TestAnnotationOne": "one", "Different": "1234"}),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.labels.%", "2"),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.labels.TestLabelOne", "one"),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.labels.TestLabelThree", "three"),
|
||||||
|
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{"TestLabelOne": "one", "TestLabelThree": "three"}),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.name", nsName),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.generation"),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.resource_version"),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.self_link"),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.uid"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Config: testAccKubernetesNamespaceConfig_noLists(nsName),
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
testAccCheckKubernetesNamespaceExists("kubernetes_namespace.test", &conf),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.annotations.%", "0"),
|
||||||
|
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{}),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.labels.%", "0"),
|
||||||
|
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{}),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.name", nsName),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.generation"),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.resource_version"),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.self_link"),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.uid"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccKubernetesNamespace_importBasic(t *testing.T) {
|
||||||
|
resourceName := "kubernetes_namespace.test"
|
||||||
|
nsName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckKubernetesNamespaceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccKubernetesNamespaceConfig_basic(nsName),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
ResourceName: resourceName,
|
||||||
|
ImportState: true,
|
||||||
|
ImportStateVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccKubernetesNamespace_generatedName(t *testing.T) {
|
||||||
|
var conf api.Namespace
|
||||||
|
prefix := "tf-acc-test-gen-"
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
IDRefreshName: "kubernetes_namespace.test",
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckKubernetesNamespaceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccKubernetesNamespaceConfig_generatedName(prefix),
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
testAccCheckKubernetesNamespaceExists("kubernetes_namespace.test", &conf),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.annotations.%", "0"),
|
||||||
|
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{}),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.labels.%", "0"),
|
||||||
|
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{}),
|
||||||
|
resource.TestCheckResourceAttr("kubernetes_namespace.test", "metadata.0.generate_name", prefix),
|
||||||
|
resource.TestMatchResourceAttr("kubernetes_namespace.test", "metadata.0.name", regexp.MustCompile("^"+prefix)),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.generation"),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.resource_version"),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.self_link"),
|
||||||
|
resource.TestCheckResourceAttrSet("kubernetes_namespace.test", "metadata.0.uid"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccKubernetesNamespace_importGeneratedName(t *testing.T) {
|
||||||
|
resourceName := "kubernetes_namespace.test"
|
||||||
|
prefix := "tf-acc-test-gen-import-"
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckKubernetesNamespaceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccKubernetesNamespaceConfig_generatedName(prefix),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
ResourceName: resourceName,
|
||||||
|
ImportState: true,
|
||||||
|
ImportStateVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckMetaAnnotations(om *api.ObjectMeta, expected map[string]string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
if len(expected) == 0 && len(om.Annotations) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(om.Annotations, expected) {
|
||||||
|
return fmt.Errorf("%s annotations don't match.\nExpected: %q\nGiven: %q",
|
||||||
|
om.Name, expected, om.Annotations)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckMetaLabels(om *api.ObjectMeta, expected map[string]string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
if len(expected) == 0 && len(om.Labels) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(om.Labels, expected) {
|
||||||
|
return fmt.Errorf("%s labels don't match.\nExpected: %q\nGiven: %q",
|
||||||
|
om.Name, expected, om.Labels)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckKubernetesNamespaceDestroy(s *terraform.State) error {
|
||||||
|
conn := testAccProvider.Meta().(*kubernetes.Clientset)
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "kubernetes_namespace" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := conn.CoreV1().Namespaces().Get(rs.Primary.ID)
|
||||||
|
if err == nil {
|
||||||
|
if resp.Name == rs.Primary.ID {
|
||||||
|
return fmt.Errorf("Namespace still exists: %s", rs.Primary.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckKubernetesNamespaceExists(n string, obj *api.Namespace) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[n]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := testAccProvider.Meta().(*kubernetes.Clientset)
|
||||||
|
out, err := conn.CoreV1().Namespaces().Get(rs.Primary.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*obj = *out
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccKubernetesNamespaceConfig_basic(nsName string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "kubernetes_namespace" "test" {
|
||||||
|
metadata {
|
||||||
|
annotations {
|
||||||
|
TestAnnotationOne = "one"
|
||||||
|
TestAnnotationTwo = "two"
|
||||||
|
}
|
||||||
|
labels {
|
||||||
|
TestLabelOne = "one"
|
||||||
|
TestLabelTwo = "two"
|
||||||
|
TestLabelThree = "three"
|
||||||
|
}
|
||||||
|
name = "%s"
|
||||||
|
}
|
||||||
|
}`, nsName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccKubernetesNamespaceConfig_smallerLists(nsName string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "kubernetes_namespace" "test" {
|
||||||
|
metadata {
|
||||||
|
annotations {
|
||||||
|
TestAnnotationOne = "one"
|
||||||
|
Different = "1234"
|
||||||
|
}
|
||||||
|
labels {
|
||||||
|
TestLabelOne = "one"
|
||||||
|
TestLabelThree = "three"
|
||||||
|
}
|
||||||
|
name = "%s"
|
||||||
|
}
|
||||||
|
}`, nsName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccKubernetesNamespaceConfig_noLists(nsName string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "kubernetes_namespace" "test" {
|
||||||
|
metadata {
|
||||||
|
name = "%s"
|
||||||
|
}
|
||||||
|
}`, nsName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccKubernetesNamespaceConfig_generatedName(prefix string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "kubernetes_namespace" "test" {
|
||||||
|
metadata {
|
||||||
|
generate_name = "%s"
|
||||||
|
}
|
||||||
|
}`, prefix)
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package kubernetes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func metadataFields(objectName string) map[string]*schema.Schema {
|
||||||
|
return map[string]*schema.Schema{
|
||||||
|
"annotations": {
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
Description: fmt.Sprintf("An unstructured key value map stored with the %s that may be used to store arbitrary metadata. More info: http://kubernetes.io/docs/user-guide/annotations", objectName),
|
||||||
|
Optional: true,
|
||||||
|
ValidateFunc: validateAnnotations,
|
||||||
|
},
|
||||||
|
"generation": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Description: "A sequence number representing a specific generation of the desired state.",
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
Description: fmt.Sprintf("Map of string keys and values that can be used to organize and categorize (scope and select) the %s. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels", objectName),
|
||||||
|
Optional: true,
|
||||||
|
ValidateFunc: validateLabels,
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: fmt.Sprintf("Name of the %s, must be unique. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names", objectName),
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Computed: true,
|
||||||
|
ValidateFunc: validateName,
|
||||||
|
ConflictsWith: []string{"metadata.generate_name"},
|
||||||
|
},
|
||||||
|
"resource_version": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: fmt.Sprintf("An opaque value that represents the internal version of this %s that can be used by clients to determine when %s has changed. Read more: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#concurrency-control-and-consistency", objectName, objectName),
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"self_link": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: fmt.Sprintf("A URL representing this %s.", objectName),
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"uid": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: fmt.Sprintf("The unique in time and space value for this %s. More info: http://kubernetes.io/docs/user-guide/identifiers#uids", objectName),
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func metadataSchema(objectName string) *schema.Schema {
|
||||||
|
fields := metadataFields(objectName)
|
||||||
|
fields["generate_name"] = &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "Prefix, used by the server, to generate a unique name ONLY IF the `name` field has not been provided. This value will also be combined with a unique suffix. Read more: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#idempotency",
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
ValidateFunc: validateGenerateName,
|
||||||
|
ConflictsWith: []string{"metadata.name"},
|
||||||
|
}
|
||||||
|
|
||||||
|
return &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Description: fmt.Sprintf("Standard %s's metadata. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#metadata", objectName),
|
||||||
|
Required: true,
|
||||||
|
MaxItems: 1,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: fields,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package kubernetes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
api "k8s.io/kubernetes/pkg/api/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func expandMetadata(in []interface{}) api.ObjectMeta {
|
||||||
|
meta := api.ObjectMeta{}
|
||||||
|
if len(in) < 1 {
|
||||||
|
return meta
|
||||||
|
}
|
||||||
|
m := in[0].(map[string]interface{})
|
||||||
|
|
||||||
|
meta.Annotations = expandStringMap(m["annotations"].(map[string]interface{}))
|
||||||
|
meta.Labels = expandStringMap(m["labels"].(map[string]interface{}))
|
||||||
|
|
||||||
|
if v, ok := m["generate_name"]; ok {
|
||||||
|
meta.GenerateName = v.(string)
|
||||||
|
}
|
||||||
|
if v, ok := m["name"]; ok {
|
||||||
|
meta.Name = v.(string)
|
||||||
|
}
|
||||||
|
if v, ok := m["namespace"]; ok {
|
||||||
|
meta.Namespace = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandStringMap(m map[string]interface{}) map[string]string {
|
||||||
|
result := make(map[string]string)
|
||||||
|
for k, v := range m {
|
||||||
|
result[k] = v.(string)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenMetadata(meta api.ObjectMeta) []map[string]interface{} {
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
m["annotations"] = meta.Annotations
|
||||||
|
m["generate_name"] = meta.GenerateName
|
||||||
|
m["labels"] = meta.Labels
|
||||||
|
m["name"] = meta.Name
|
||||||
|
m["resource_version"] = meta.ResourceVersion
|
||||||
|
m["self_link"] = meta.SelfLink
|
||||||
|
m["uid"] = fmt.Sprintf("%v", meta.UID)
|
||||||
|
m["generation"] = meta.Generation
|
||||||
|
|
||||||
|
if meta.Namespace != "" {
|
||||||
|
m["namespace"] = meta.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
return []map[string]interface{}{m}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
provider "google" {
|
||||||
|
// Provider settings to be provided via ENV variables
|
||||||
|
}
|
||||||
|
|
||||||
|
data "google_compute_zones" "available" {}
|
||||||
|
|
||||||
|
resource "random_id" "cluster_name" {
|
||||||
|
byte_length = 10
|
||||||
|
}
|
||||||
|
resource "random_id" "username" {
|
||||||
|
byte_length = 14
|
||||||
|
}
|
||||||
|
resource "random_id" "password" {
|
||||||
|
byte_length = 16
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_container_cluster" "primary" {
|
||||||
|
name = "tf-acc-test-${random_id.cluster_name.hex}"
|
||||||
|
zone = "${data.google_compute_zones.available.names[0]}"
|
||||||
|
initial_node_count = 3
|
||||||
|
|
||||||
|
additional_zones = [
|
||||||
|
"${data.google_compute_zones.available.names[1]}"
|
||||||
|
]
|
||||||
|
|
||||||
|
master_auth {
|
||||||
|
username = "${random_id.username.hex}"
|
||||||
|
password = "${random_id.password.hex}"
|
||||||
|
}
|
||||||
|
|
||||||
|
node_config {
|
||||||
|
oauth_scopes = [
|
||||||
|
"https://www.googleapis.com/auth/compute",
|
||||||
|
"https://www.googleapis.com/auth/devstorage.read_only",
|
||||||
|
"https://www.googleapis.com/auth/logging.write",
|
||||||
|
"https://www.googleapis.com/auth/monitoring"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output "endpoint" {
|
||||||
|
value = "${google_container_cluster.primary.endpoint}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "username" {
|
||||||
|
value = "${google_container_cluster.primary.master_auth.0.username}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "password" {
|
||||||
|
value = "${google_container_cluster.primary.master_auth.0.password}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "client_certificate_b64" {
|
||||||
|
value = "${google_container_cluster.primary.master_auth.0.client_certificate}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "client_key_b64" {
|
||||||
|
value = "${google_container_cluster.primary.master_auth.0.client_key}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "cluster_ca_certificate_b64" {
|
||||||
|
value = "${google_container_cluster.primary.master_auth.0.cluster_ca_certificate}"
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package kubernetes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
apiValidation "k8s.io/kubernetes/pkg/api/validation"
|
||||||
|
utilValidation "k8s.io/kubernetes/pkg/util/validation"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateAnnotations(value interface{}, key string) (ws []string, es []error) {
|
||||||
|
m := value.(map[string]interface{})
|
||||||
|
for k, _ := range m {
|
||||||
|
errors := utilValidation.IsQualifiedName(strings.ToLower(k))
|
||||||
|
if len(errors) > 0 {
|
||||||
|
for _, e := range errors {
|
||||||
|
es = append(es, fmt.Errorf("%s (%q) %s", key, k, e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateName(value interface{}, key string) (ws []string, es []error) {
|
||||||
|
v := value.(string)
|
||||||
|
|
||||||
|
errors := apiValidation.NameIsDNSLabel(v, false)
|
||||||
|
if len(errors) > 0 {
|
||||||
|
for _, err := range errors {
|
||||||
|
es = append(es, fmt.Errorf("%s %s", key, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateGenerateName(value interface{}, key string) (ws []string, es []error) {
|
||||||
|
v := value.(string)
|
||||||
|
|
||||||
|
errors := apiValidation.NameIsDNSLabel(v, true)
|
||||||
|
if len(errors) > 0 {
|
||||||
|
for _, err := range errors {
|
||||||
|
es = append(es, fmt.Errorf("%s %s", key, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateLabels(value interface{}, key string) (ws []string, es []error) {
|
||||||
|
m := value.(map[string]interface{})
|
||||||
|
for k, v := range m {
|
||||||
|
for _, msg := range utilValidation.IsQualifiedName(k) {
|
||||||
|
es = append(es, fmt.Errorf("%s (%q) %s", key, k, msg))
|
||||||
|
}
|
||||||
|
val := v.(string)
|
||||||
|
for _, msg := range utilValidation.IsValidLabelValue(val) {
|
||||||
|
es = append(es, fmt.Errorf("%s (%q) %s", key, val, msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ import (
|
||||||
icinga2provider "github.com/hashicorp/terraform/builtin/providers/icinga2"
|
icinga2provider "github.com/hashicorp/terraform/builtin/providers/icinga2"
|
||||||
ignitionprovider "github.com/hashicorp/terraform/builtin/providers/ignition"
|
ignitionprovider "github.com/hashicorp/terraform/builtin/providers/ignition"
|
||||||
influxdbprovider "github.com/hashicorp/terraform/builtin/providers/influxdb"
|
influxdbprovider "github.com/hashicorp/terraform/builtin/providers/influxdb"
|
||||||
|
kubernetesprovider "github.com/hashicorp/terraform/builtin/providers/kubernetes"
|
||||||
libratoprovider "github.com/hashicorp/terraform/builtin/providers/librato"
|
libratoprovider "github.com/hashicorp/terraform/builtin/providers/librato"
|
||||||
logentriesprovider "github.com/hashicorp/terraform/builtin/providers/logentries"
|
logentriesprovider "github.com/hashicorp/terraform/builtin/providers/logentries"
|
||||||
mailgunprovider "github.com/hashicorp/terraform/builtin/providers/mailgun"
|
mailgunprovider "github.com/hashicorp/terraform/builtin/providers/mailgun"
|
||||||
|
@ -112,6 +113,7 @@ var InternalProviders = map[string]plugin.ProviderFunc{
|
||||||
"icinga2": icinga2provider.Provider,
|
"icinga2": icinga2provider.Provider,
|
||||||
"ignition": ignitionprovider.Provider,
|
"ignition": ignitionprovider.Provider,
|
||||||
"influxdb": influxdbprovider.Provider,
|
"influxdb": influxdbprovider.Provider,
|
||||||
|
"kubernetes": kubernetesprovider.Provider,
|
||||||
"librato": libratoprovider.Provider,
|
"librato": libratoprovider.Provider,
|
||||||
"logentries": logentriesprovider.Provider,
|
"logentries": logentriesprovider.Provider,
|
||||||
"mailgun": mailgunprovider.Provider,
|
"mailgun": mailgunprovider.Provider,
|
||||||
|
|
|
@ -40,6 +40,7 @@ body.layout-heroku,
|
||||||
body.layout-ignition,
|
body.layout-ignition,
|
||||||
body.layout-icinga2,
|
body.layout-icinga2,
|
||||||
body.layout-influxdb,
|
body.layout-influxdb,
|
||||||
|
body.layout-kubernetes,
|
||||||
body.layout-librato,
|
body.layout-librato,
|
||||||
body.layout-logentries,
|
body.layout-logentries,
|
||||||
body.layout-mailgun,
|
body.layout-mailgun,
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
---
|
||||||
|
layout: "kubernetes"
|
||||||
|
page_title: "Provider: Kubernetes"
|
||||||
|
sidebar_current: "docs-kubernetes-index"
|
||||||
|
description: |-
|
||||||
|
The Kubernetes (K8s) provider is used to interact with the resources supported by Kubernetes. The provider needs to be configured with the proper credentials before it can be used.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Kubernetes Provider
|
||||||
|
|
||||||
|
The Kubernetes (K8S) provider is used to interact with the resources supported by Kubernetes. The provider needs to be configured with the proper credentials before it can be used.
|
||||||
|
|
||||||
|
Use the navigation to the left to read about the available resources.
|
||||||
|
|
||||||
|
-> **Note:** The Kubernetes provider is new as of Terraform 0.9. It is ready to be used but many features are still being added. If there is a Kubernetes feature missing, please report it in the GitHub repo.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
provider "kubernetes" {
|
||||||
|
config_context_auth_info = "ops"
|
||||||
|
config_context_cluster = "mycluster"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "kubernetes_namespace" "example" {
|
||||||
|
metadata {
|
||||||
|
name = "my-first-namespace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
There are generally two ways to configure the Kubernetes provider.
|
||||||
|
|
||||||
|
The provider always first tries to load **a config file** from a given
|
||||||
|
(or default) location - this requires valid `config_context_auth_info` & `config_context_cluster`.
|
||||||
|
|
||||||
|
The other way is **statically** define all the credentials:
|
||||||
|
|
||||||
|
```
|
||||||
|
provider "kubernetes" {
|
||||||
|
host = "https://104.196.242.174"
|
||||||
|
username = "ClusterMaster"
|
||||||
|
password = "MindTheGap"
|
||||||
|
client_certificate = "${file("~/.kube/client-cert.pem")}"
|
||||||
|
client_key = "${file("~/.kube/client-key.pem")}"
|
||||||
|
cluster_ca_certificate = "${file("~/.kube/cluster-ca-cert.pem")}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have **both** valid configuration in a config file and static configuration, the static one is used as override.
|
||||||
|
i.e. any static field will override its counterpart loaded from the config.
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `host` - (Optional) The hostname (in form of URI) of Kubernetes master. Can be sourced from `KUBE_HOST`. Defaults to `https://localhost`.
|
||||||
|
* `username` - (Optional) The username to use for HTTP basic authentication when accessing the Kubernetes master endpoint. Can be sourced from `KUBE_USER`.
|
||||||
|
* `password` - (Optional) The password to use for HTTP basic authentication when accessing the Kubernetes master endpoint. Can be sourced from `KUBE_PASSWORD`.
|
||||||
|
* `insecure`- (Optional) Whether server should be accessed without verifying the TLS certificate. Can be sourced from `KUBE_INSECURE`. Defaults to `false`.
|
||||||
|
* `client_certificate` - (Optional) PEM-encoded client certificate for TLS authentication. Can be sourced from `KUBE_CLIENT_CERT_DATA`.
|
||||||
|
* `client_key` - (Optional) PEM-encoded client certificate key for TLS authentication. Can be sourced from `KUBE_CLIENT_KEY_DATA`.
|
||||||
|
* `cluster_ca_certificate` - (Optional) PEM-encoded root certificates bundle for TLS authentication. Can be sourced from `KUBE_CLUSTER_CA_CERT_DATA`.
|
||||||
|
* `config_path` - (Optional) Path to the kube config file. Can be sourced from `KUBE_CONFIG`. Defaults to `~/.kube/config`.
|
||||||
|
* `config_context_auth_info` - (Optional) Authentication info context of the kube config (name of the kubeconfig user, `--user` flag in `kubectl`). Can be sourced from `KUBE_CTX_AUTH_INFO`.
|
||||||
|
* `config_context_cluster` - (Optional) Cluster context of the kube config (name of the kubeconfig cluster, `--cluster` flag in `kubectl`). Can be sourced from `KUBE_CTX_CLUSTER`.
|
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
layout: "kubernetes"
|
||||||
|
page_title: "Kubernetes: kubernetes_namespace"
|
||||||
|
sidebar_current: "docs-kubernetes-resource-namespace"
|
||||||
|
description: |-
|
||||||
|
Kubernetes supports multiple virtual clusters backed by the same physical cluster. These virtual clusters are called namespaces.
|
||||||
|
---
|
||||||
|
|
||||||
|
# kubernetes_namespace
|
||||||
|
|
||||||
|
Kubernetes supports multiple virtual clusters backed by the same physical cluster. These virtual clusters are called namespaces.
|
||||||
|
Read more about namespaces at https://kubernetes.io/docs/user-guide/namespaces/
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
resource "kubernetes_namespace" "example" {
|
||||||
|
metadata {
|
||||||
|
annotations {
|
||||||
|
name = "example-annotation"
|
||||||
|
}
|
||||||
|
labels {
|
||||||
|
mylabel = "label-value"
|
||||||
|
}
|
||||||
|
name = "TerraformExampleNamespace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `metadata` - (Required) Standard namespace's [metadata](https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#metadata).
|
||||||
|
|
||||||
|
## Nested Blocks
|
||||||
|
|
||||||
|
### `metadata`
|
||||||
|
|
||||||
|
#### Arguments
|
||||||
|
|
||||||
|
* `annotations` - (Optional) An unstructured key value map stored with the namespace that may be used to store arbitrary metadata. More info: http://kubernetes.io/docs/user-guide/annotations
|
||||||
|
* `generate_name` - (Optional) Prefix, used by the server, to generate a unique name ONLY IF the `name` field has not been provided. This value will also be combined with a unique suffix. Read more about [name idempotency](https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#idempotency).
|
||||||
|
* `labels` - (Optional) Map of string keys and values that can be used to organize and categorize (scope and select) namespaces. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels
|
||||||
|
* `name` - (Optional) Name of the namespace, must be unique. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names
|
||||||
|
|
||||||
|
#### Attributes
|
||||||
|
|
||||||
|
* `generation` - A sequence number representing a specific generation of the desired state.
|
||||||
|
* `resource_version` - An opaque value that represents the internal version of this namespace that can be used by clients to determine when namespaces have changed. Read more about [concurrency control and consistency](https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#concurrency-control-and-consistency).
|
||||||
|
* `self_link` - A URL representing this namespace.
|
||||||
|
* `uid` - The unique in time and space value for this namespace. More info: http://kubernetes.io/docs/user-guide/identifiers#uids
|
||||||
|
|
||||||
|
## Import
|
||||||
|
|
||||||
|
Namespaces can be imported using their name, e.g.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform import kubernetes_namespace.n TerraformExampleNamespace
|
||||||
|
```
|
|
@ -303,6 +303,10 @@
|
||||||
<a href="/docs/providers/influxdb/index.html">InfluxDB</a>
|
<a href="/docs/providers/influxdb/index.html">InfluxDB</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-providers-kubernetes") %>>
|
||||||
|
<a href="/docs/providers/kubernetes/index.html">Kubernetes</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-providers-librato") %>>
|
<li<%= sidebar_current("docs-providers-librato") %>>
|
||||||
<a href="/docs/providers/librato/index.html">Librato</a>
|
<a href="/docs/providers/librato/index.html">Librato</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<% wrap_layout :inner do %>
|
||||||
|
<% content_for :sidebar do %>
|
||||||
|
<div class="docs-sidebar hidden-print affix-top" role="complementary">
|
||||||
|
<ul class="nav docs-sidenav">
|
||||||
|
<li<%= sidebar_current("docs-home") %>>
|
||||||
|
<a href="/docs/providers/index.html">« Documentation Home</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-kubernetes-index") %>>
|
||||||
|
<a href="/docs/providers/kubernetes/index.html">Kubernetes Provider</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current(/^docs-kubernetes-resource/) %>>
|
||||||
|
<a href="#">Resources</a>
|
||||||
|
<ul class="nav nav-visible">
|
||||||
|
<li<%= sidebar_current("docs-kubernetes-resource-namespace") %>>
|
||||||
|
<a href="/docs/providers/kubernetes/r/namespace.html">kubernetes_namespace</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= yield %>
|
||||||
|
<% end %>
|
Loading…
Reference in New Issue