github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/kubernetes/resource_kubernetes_resource_quota.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  	"k8s.io/apimachinery/pkg/api/errors"
    11  	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	pkgApi "k8s.io/apimachinery/pkg/types"
    13  	api "k8s.io/kubernetes/pkg/api/v1"
    14  	kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
    15  )
    16  
    17  func resourceKubernetesResourceQuota() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceKubernetesResourceQuotaCreate,
    20  		Read:   resourceKubernetesResourceQuotaRead,
    21  		Exists: resourceKubernetesResourceQuotaExists,
    22  		Update: resourceKubernetesResourceQuotaUpdate,
    23  		Delete: resourceKubernetesResourceQuotaDelete,
    24  		Importer: &schema.ResourceImporter{
    25  			State: schema.ImportStatePassthrough,
    26  		},
    27  
    28  		Schema: map[string]*schema.Schema{
    29  			"metadata": namespacedMetadataSchema("resource quota", true),
    30  			"spec": {
    31  				Type:        schema.TypeList,
    32  				Description: "Spec defines the desired quota. http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status",
    33  				Optional:    true,
    34  				MaxItems:    1,
    35  				Elem: &schema.Resource{
    36  					Schema: map[string]*schema.Schema{
    37  						"hard": {
    38  							Type:         schema.TypeMap,
    39  							Description:  "The set of desired hard limits for each named resource. More info: http://releases.k8s.io/HEAD/docs/design/admission_control_resource_quota.md#admissioncontrol-plugin-resourcequota",
    40  							Optional:     true,
    41  							Elem:         schema.TypeString,
    42  							ValidateFunc: validateResourceList,
    43  						},
    44  						"scopes": {
    45  							Type:        schema.TypeSet,
    46  							Description: "A collection of filters that must match each object tracked by a quota. If not specified, the quota matches all objects.",
    47  							Optional:    true,
    48  							ForceNew:    true,
    49  							Elem:        &schema.Schema{Type: schema.TypeString},
    50  							Set:         schema.HashString,
    51  						},
    52  					},
    53  				},
    54  			},
    55  		},
    56  	}
    57  }
    58  
    59  func resourceKubernetesResourceQuotaCreate(d *schema.ResourceData, meta interface{}) error {
    60  	conn := meta.(*kubernetes.Clientset)
    61  
    62  	metadata := expandMetadata(d.Get("metadata").([]interface{}))
    63  	spec, err := expandResourceQuotaSpec(d.Get("spec").([]interface{}))
    64  	if err != nil {
    65  		return err
    66  	}
    67  	resQuota := api.ResourceQuota{
    68  		ObjectMeta: metadata,
    69  		Spec:       spec,
    70  	}
    71  	log.Printf("[INFO] Creating new resource quota: %#v", resQuota)
    72  	out, err := conn.CoreV1().ResourceQuotas(metadata.Namespace).Create(&resQuota)
    73  	if err != nil {
    74  		return fmt.Errorf("Failed to create resource quota: %s", err)
    75  	}
    76  	log.Printf("[INFO] Submitted new resource quota: %#v", out)
    77  	d.SetId(buildId(out.ObjectMeta))
    78  
    79  	err = resource.Retry(1*time.Minute, func() *resource.RetryError {
    80  		quota, err := conn.CoreV1().ResourceQuotas(out.Namespace).Get(out.Name, meta_v1.GetOptions{})
    81  		if err != nil {
    82  			return resource.NonRetryableError(err)
    83  		}
    84  		if resourceListEquals(spec.Hard, quota.Status.Hard) {
    85  			return nil
    86  		}
    87  		err = fmt.Errorf("Quotas don't match after creation.\nExpected: %#v\nGiven: %#v",
    88  			spec.Hard, quota.Status.Hard)
    89  		return resource.RetryableError(err)
    90  	})
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	return resourceKubernetesResourceQuotaRead(d, meta)
    96  }
    97  
    98  func resourceKubernetesResourceQuotaRead(d *schema.ResourceData, meta interface{}) error {
    99  	conn := meta.(*kubernetes.Clientset)
   100  
   101  	namespace, name := idParts(d.Id())
   102  	log.Printf("[INFO] Reading resource quota %s", name)
   103  	resQuota, err := conn.CoreV1().ResourceQuotas(namespace).Get(name, meta_v1.GetOptions{})
   104  	if err != nil {
   105  		log.Printf("[DEBUG] Received error: %#v", err)
   106  		return err
   107  	}
   108  	log.Printf("[INFO] Received resource quota: %#v", resQuota)
   109  
   110  	// This is to work around K8S bug
   111  	// See https://github.com/kubernetes/kubernetes/issues/44539
   112  	if resQuota.ObjectMeta.GenerateName == "" {
   113  		if v, ok := d.GetOk("metadata.0.generate_name"); ok {
   114  			resQuota.ObjectMeta.GenerateName = v.(string)
   115  		}
   116  	}
   117  
   118  	err = d.Set("metadata", flattenMetadata(resQuota.ObjectMeta))
   119  	if err != nil {
   120  		return err
   121  	}
   122  	err = d.Set("spec", flattenResourceQuotaSpec(resQuota.Spec))
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	return nil
   128  }
   129  
   130  func resourceKubernetesResourceQuotaUpdate(d *schema.ResourceData, meta interface{}) error {
   131  	conn := meta.(*kubernetes.Clientset)
   132  
   133  	namespace, name := idParts(d.Id())
   134  
   135  	ops := patchMetadata("metadata.0.", "/metadata/", d)
   136  	var spec api.ResourceQuotaSpec
   137  	waitForChangedSpec := false
   138  	if d.HasChange("spec") {
   139  		var err error
   140  		spec, err = expandResourceQuotaSpec(d.Get("spec").([]interface{}))
   141  		if err != nil {
   142  			return err
   143  		}
   144  		ops = append(ops, &ReplaceOperation{
   145  			Path:  "/spec",
   146  			Value: spec,
   147  		})
   148  		waitForChangedSpec = true
   149  	}
   150  	data, err := ops.MarshalJSON()
   151  	if err != nil {
   152  		return fmt.Errorf("Failed to marshal update operations: %s", err)
   153  	}
   154  	log.Printf("[INFO] Updating resource quota %q: %v", name, string(data))
   155  	out, err := conn.CoreV1().ResourceQuotas(namespace).Patch(name, pkgApi.JSONPatchType, data)
   156  	if err != nil {
   157  		return fmt.Errorf("Failed to update resource quota: %s", err)
   158  	}
   159  	log.Printf("[INFO] Submitted updated resource quota: %#v", out)
   160  	d.SetId(buildId(out.ObjectMeta))
   161  
   162  	if waitForChangedSpec {
   163  		err = resource.Retry(1*time.Minute, func() *resource.RetryError {
   164  			quota, err := conn.CoreV1().ResourceQuotas(namespace).Get(name, meta_v1.GetOptions{})
   165  			if err != nil {
   166  				return resource.NonRetryableError(err)
   167  			}
   168  			if resourceListEquals(spec.Hard, quota.Status.Hard) {
   169  				return nil
   170  			}
   171  			err = fmt.Errorf("Quotas don't match after update.\nExpected: %#v\nGiven: %#v",
   172  				spec.Hard, quota.Status.Hard)
   173  			return resource.RetryableError(err)
   174  		})
   175  		if err != nil {
   176  			return err
   177  		}
   178  	}
   179  
   180  	return resourceKubernetesResourceQuotaRead(d, meta)
   181  }
   182  
   183  func resourceKubernetesResourceQuotaDelete(d *schema.ResourceData, meta interface{}) error {
   184  	conn := meta.(*kubernetes.Clientset)
   185  
   186  	namespace, name := idParts(d.Id())
   187  	log.Printf("[INFO] Deleting resource quota: %#v", name)
   188  	err := conn.CoreV1().ResourceQuotas(namespace).Delete(name, &meta_v1.DeleteOptions{})
   189  	if err != nil {
   190  		return err
   191  	}
   192  
   193  	log.Printf("[INFO] Resource quota %s deleted", name)
   194  
   195  	d.SetId("")
   196  	return nil
   197  }
   198  
   199  func resourceKubernetesResourceQuotaExists(d *schema.ResourceData, meta interface{}) (bool, error) {
   200  	conn := meta.(*kubernetes.Clientset)
   201  
   202  	namespace, name := idParts(d.Id())
   203  	log.Printf("[INFO] Checking resource quota %s", name)
   204  	_, err := conn.CoreV1().ResourceQuotas(namespace).Get(name, meta_v1.GetOptions{})
   205  	if err != nil {
   206  		if statusErr, ok := err.(*errors.StatusError); ok && statusErr.ErrStatus.Code == 404 {
   207  			return false, nil
   208  		}
   209  		log.Printf("[DEBUG] Received error: %#v", err)
   210  	}
   211  	return true, err
   212  }