github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/kubernetes/resource_kubernetes_persistent_volume_claim.go (about)

     1  package kubernetes
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"time"
     7  
     8  	"github.com/hashicorp/terraform/helper/resource"
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  	pkgApi "k8s.io/kubernetes/pkg/api"
    11  	"k8s.io/kubernetes/pkg/api/errors"
    12  	api "k8s.io/kubernetes/pkg/api/v1"
    13  	kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
    14  )
    15  
    16  func resourceKubernetesPersistentVolumeClaim() *schema.Resource {
    17  	return &schema.Resource{
    18  		Create: resourceKubernetesPersistentVolumeClaimCreate,
    19  		Read:   resourceKubernetesPersistentVolumeClaimRead,
    20  		Exists: resourceKubernetesPersistentVolumeClaimExists,
    21  		Update: resourceKubernetesPersistentVolumeClaimUpdate,
    22  		Delete: resourceKubernetesPersistentVolumeClaimDelete,
    23  		Importer: &schema.ResourceImporter{
    24  			State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
    25  				d.Set("wait_until_bound", true)
    26  				return []*schema.ResourceData{d}, nil
    27  			},
    28  		},
    29  
    30  		Timeouts: &schema.ResourceTimeout{
    31  			Create: schema.DefaultTimeout(5 * time.Minute),
    32  		},
    33  
    34  		Schema: map[string]*schema.Schema{
    35  			"metadata": namespacedMetadataSchema("persistent volume claim", true),
    36  			"spec": {
    37  				Type:        schema.TypeList,
    38  				Description: "Spec defines the desired characteristics of a volume requested by a pod author. More info: http://kubernetes.io/docs/user-guide/persistent-volumes#persistentvolumeclaims",
    39  				Required:    true,
    40  				ForceNew:    true,
    41  				MaxItems:    1,
    42  				Elem: &schema.Resource{
    43  					Schema: map[string]*schema.Schema{
    44  						"access_modes": {
    45  							Type:        schema.TypeSet,
    46  							Description: "A set of the desired access modes the volume should have. More info: http://kubernetes.io/docs/user-guide/persistent-volumes#access-modes-1",
    47  							Required:    true,
    48  							ForceNew:    true,
    49  							Elem:        &schema.Schema{Type: schema.TypeString},
    50  							Set:         schema.HashString,
    51  						},
    52  						"resources": {
    53  							Type:        schema.TypeList,
    54  							Description: "A list of the minimum resources the volume should have. More info: http://kubernetes.io/docs/user-guide/persistent-volumes#resources",
    55  							Required:    true,
    56  							ForceNew:    true,
    57  							MaxItems:    1,
    58  							Elem: &schema.Resource{
    59  								Schema: map[string]*schema.Schema{
    60  									"limits": {
    61  										Type:        schema.TypeMap,
    62  										Description: "Map describing the maximum amount of compute resources allowed. More info: http://kubernetes.io/docs/user-guide/compute-resources/",
    63  										Optional:    true,
    64  										ForceNew:    true,
    65  									},
    66  									"requests": {
    67  										Type:        schema.TypeMap,
    68  										Description: "Map describing the minimum amount of compute resources required. If this is omitted for a container, it defaults to `limits` if that is explicitly specified, otherwise to an implementation-defined value. More info: http://kubernetes.io/docs/user-guide/compute-resources/",
    69  										Optional:    true,
    70  										ForceNew:    true,
    71  									},
    72  								},
    73  							},
    74  						},
    75  						"selector": {
    76  							Type:        schema.TypeList,
    77  							Description: "A label query over volumes to consider for binding.",
    78  							Optional:    true,
    79  							ForceNew:    true,
    80  							MaxItems:    1,
    81  							Elem: &schema.Resource{
    82  								Schema: map[string]*schema.Schema{
    83  									"match_expressions": {
    84  										Type:        schema.TypeList,
    85  										Description: "A list of label selector requirements. The requirements are ANDed.",
    86  										Optional:    true,
    87  										ForceNew:    true,
    88  										Elem: &schema.Resource{
    89  											Schema: map[string]*schema.Schema{
    90  												"key": {
    91  													Type:        schema.TypeString,
    92  													Description: "The label key that the selector applies to.",
    93  													Optional:    true,
    94  													ForceNew:    true,
    95  												},
    96  												"operator": {
    97  													Type:        schema.TypeString,
    98  													Description: "A key's relationship to a set of values. Valid operators ard `In`, `NotIn`, `Exists` and `DoesNotExist`.",
    99  													Optional:    true,
   100  													ForceNew:    true,
   101  												},
   102  												"values": {
   103  													Type:        schema.TypeSet,
   104  													Description: "An array of string values. If the operator is `In` or `NotIn`, the values array must be non-empty. If the operator is `Exists` or `DoesNotExist`, the values array must be empty. This array is replaced during a strategic merge patch.",
   105  													Optional:    true,
   106  													ForceNew:    true,
   107  													Elem:        &schema.Schema{Type: schema.TypeString},
   108  													Set:         schema.HashString,
   109  												},
   110  											},
   111  										},
   112  									},
   113  									"match_labels": {
   114  										Type:        schema.TypeMap,
   115  										Description: "A map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of `match_expressions`, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
   116  										Optional:    true,
   117  										ForceNew:    true,
   118  									},
   119  								},
   120  							},
   121  						},
   122  						"volume_name": {
   123  							Type:        schema.TypeString,
   124  							Description: "The binding reference to the PersistentVolume backing this claim.",
   125  							Optional:    true,
   126  							ForceNew:    true,
   127  							Computed:    true,
   128  						},
   129  					},
   130  				},
   131  			},
   132  			"wait_until_bound": {
   133  				Type:        schema.TypeBool,
   134  				Description: "Whether to wait for the claim to reach `Bound` state (to find volume in which to claim the space)",
   135  				Optional:    true,
   136  				Default:     true,
   137  			},
   138  		},
   139  	}
   140  }
   141  
   142  func resourceKubernetesPersistentVolumeClaimCreate(d *schema.ResourceData, meta interface{}) error {
   143  	conn := meta.(*kubernetes.Clientset)
   144  
   145  	metadata := expandMetadata(d.Get("metadata").([]interface{}))
   146  	spec, err := expandPersistentVolumeClaimSpec(d.Get("spec").([]interface{}))
   147  	if err != nil {
   148  		return err
   149  	}
   150  
   151  	claim := api.PersistentVolumeClaim{
   152  		ObjectMeta: metadata,
   153  		Spec:       spec,
   154  	}
   155  
   156  	log.Printf("[INFO] Creating new persistent volume claim: %#v", claim)
   157  	out, err := conn.CoreV1().PersistentVolumeClaims(metadata.Namespace).Create(&claim)
   158  	if err != nil {
   159  		return err
   160  	}
   161  	log.Printf("[INFO] Submitted new persistent volume claim: %#v", out)
   162  
   163  	d.SetId(buildId(out.ObjectMeta))
   164  	name := out.ObjectMeta.Name
   165  
   166  	if d.Get("wait_until_bound").(bool) {
   167  		stateConf := &resource.StateChangeConf{
   168  			Target:  []string{"Bound"},
   169  			Pending: []string{"Pending"},
   170  			Timeout: d.Timeout(schema.TimeoutCreate),
   171  			Refresh: func() (interface{}, string, error) {
   172  				out, err := conn.CoreV1().PersistentVolumeClaims(metadata.Namespace).Get(name)
   173  				if err != nil {
   174  					log.Printf("[ERROR] Received error: %#v", err)
   175  					return out, "", err
   176  				}
   177  
   178  				statusPhase := fmt.Sprintf("%v", out.Status.Phase)
   179  				log.Printf("[DEBUG] Persistent volume claim %s status received: %#v", out.Name, statusPhase)
   180  				return out, statusPhase, nil
   181  			},
   182  		}
   183  		_, err = stateConf.WaitForState()
   184  		if err != nil {
   185  			return err
   186  		}
   187  	}
   188  	log.Printf("[INFO] Persistent volume claim %s created", out.Name)
   189  
   190  	return resourceKubernetesPersistentVolumeClaimRead(d, meta)
   191  }
   192  
   193  func resourceKubernetesPersistentVolumeClaimRead(d *schema.ResourceData, meta interface{}) error {
   194  	conn := meta.(*kubernetes.Clientset)
   195  
   196  	namespace, name := idParts(d.Id())
   197  	log.Printf("[INFO] Reading persistent volume claim %s", name)
   198  	claim, err := conn.CoreV1().PersistentVolumeClaims(namespace).Get(name)
   199  	if err != nil {
   200  		log.Printf("[DEBUG] Received error: %#v", err)
   201  		return err
   202  	}
   203  	log.Printf("[INFO] Received persistent volume claim: %#v", claim)
   204  	err = d.Set("metadata", flattenMetadata(claim.ObjectMeta))
   205  	if err != nil {
   206  		return err
   207  	}
   208  	err = d.Set("spec", flattenPersistentVolumeClaimSpec(claim.Spec))
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	return nil
   214  }
   215  
   216  func resourceKubernetesPersistentVolumeClaimUpdate(d *schema.ResourceData, meta interface{}) error {
   217  	conn := meta.(*kubernetes.Clientset)
   218  	namespace, name := idParts(d.Id())
   219  
   220  	ops := patchMetadata("metadata.0.", "/metadata/", d)
   221  	// The whole spec is ForceNew = nothing to update there
   222  	data, err := ops.MarshalJSON()
   223  	if err != nil {
   224  		return fmt.Errorf("Failed to marshal update operations: %s", err)
   225  	}
   226  
   227  	log.Printf("[INFO] Updating persistent volume claim: %s", ops)
   228  	out, err := conn.CoreV1().PersistentVolumeClaims(namespace).Patch(name, pkgApi.JSONPatchType, data)
   229  	if err != nil {
   230  		return err
   231  	}
   232  	log.Printf("[INFO] Submitted updated persistent volume claim: %#v", out)
   233  
   234  	return resourceKubernetesPersistentVolumeClaimRead(d, meta)
   235  }
   236  
   237  func resourceKubernetesPersistentVolumeClaimDelete(d *schema.ResourceData, meta interface{}) error {
   238  	conn := meta.(*kubernetes.Clientset)
   239  
   240  	namespace, name := idParts(d.Id())
   241  	log.Printf("[INFO] Deleting persistent volume claim: %#v", name)
   242  	err := conn.CoreV1().PersistentVolumeClaims(namespace).Delete(name, &api.DeleteOptions{})
   243  	if err != nil {
   244  		return err
   245  	}
   246  
   247  	log.Printf("[INFO] Persistent volume claim %s deleted", name)
   248  
   249  	d.SetId("")
   250  	return nil
   251  }
   252  
   253  func resourceKubernetesPersistentVolumeClaimExists(d *schema.ResourceData, meta interface{}) (bool, error) {
   254  	conn := meta.(*kubernetes.Clientset)
   255  
   256  	namespace, name := idParts(d.Id())
   257  	log.Printf("[INFO] Checking persistent volume claim %s", name)
   258  	_, err := conn.CoreV1().PersistentVolumeClaims(namespace).Get(name)
   259  	if err != nil {
   260  		if statusErr, ok := err.(*errors.StatusError); ok && statusErr.ErrStatus.Code == 404 {
   261  			return false, nil
   262  		}
   263  		log.Printf("[DEBUG] Received error: %#v", err)
   264  	}
   265  	return true, err
   266  }