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 }