provider/kubernetes: Add support for resource_quota (#13914)

This commit is contained in:
Radek Simko 2017-05-08 13:43:24 +02:00 committed by GitHub
parent 7c4e2c198a
commit 296a3b2381
7 changed files with 737 additions and 11 deletions

View File

@ -90,6 +90,7 @@ func Provider() terraform.ResourceProvider {
"kubernetes_namespace": resourceKubernetesNamespace(), "kubernetes_namespace": resourceKubernetesNamespace(),
"kubernetes_persistent_volume": resourceKubernetesPersistentVolume(), "kubernetes_persistent_volume": resourceKubernetesPersistentVolume(),
"kubernetes_persistent_volume_claim": resourceKubernetesPersistentVolumeClaim(), "kubernetes_persistent_volume_claim": resourceKubernetesPersistentVolumeClaim(),
"kubernetes_resource_quota": resourceKubernetesResourceQuota(),
"kubernetes_secret": resourceKubernetesSecret(), "kubernetes_secret": resourceKubernetesSecret(),
}, },
ConfigureFunc: providerConfigure, ConfigureFunc: providerConfigure,

View File

@ -0,0 +1,211 @@
package kubernetes
import (
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
pkgApi "k8s.io/kubernetes/pkg/api"
"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 resourceKubernetesResourceQuota() *schema.Resource {
return &schema.Resource{
Create: resourceKubernetesResourceQuotaCreate,
Read: resourceKubernetesResourceQuotaRead,
Exists: resourceKubernetesResourceQuotaExists,
Update: resourceKubernetesResourceQuotaUpdate,
Delete: resourceKubernetesResourceQuotaDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"metadata": namespacedMetadataSchema("resource quota", true),
"spec": {
Type: schema.TypeList,
Description: "Spec defines the desired quota. http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status",
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"hard": {
Type: schema.TypeMap,
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",
Optional: true,
Elem: schema.TypeString,
ValidateFunc: validateResourceList,
},
"scopes": {
Type: schema.TypeSet,
Description: "A collection of filters that must match each object tracked by a quota. If not specified, the quota matches all objects.",
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
},
},
},
}
}
func resourceKubernetesResourceQuotaCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*kubernetes.Clientset)
metadata := expandMetadata(d.Get("metadata").([]interface{}))
spec, err := expandResourceQuotaSpec(d.Get("spec").([]interface{}))
if err != nil {
return err
}
resQuota := api.ResourceQuota{
ObjectMeta: metadata,
Spec: spec,
}
log.Printf("[INFO] Creating new resource quota: %#v", resQuota)
out, err := conn.CoreV1().ResourceQuotas(metadata.Namespace).Create(&resQuota)
if err != nil {
return fmt.Errorf("Failed to create resource quota: %s", err)
}
log.Printf("[INFO] Submitted new resource quota: %#v", out)
d.SetId(buildId(out.ObjectMeta))
err = resource.Retry(1*time.Minute, func() *resource.RetryError {
quota, err := conn.CoreV1().ResourceQuotas(out.Namespace).Get(out.Name)
if err != nil {
return resource.NonRetryableError(err)
}
if resourceListEquals(spec.Hard, quota.Status.Hard) {
return nil
}
err = fmt.Errorf("Quotas don't match after creation.\nExpected: %#v\nGiven: %#v",
spec.Hard, quota.Status.Hard)
return resource.RetryableError(err)
})
if err != nil {
return err
}
return resourceKubernetesResourceQuotaRead(d, meta)
}
func resourceKubernetesResourceQuotaRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*kubernetes.Clientset)
namespace, name := idParts(d.Id())
log.Printf("[INFO] Reading resource quota %s", name)
resQuota, err := conn.CoreV1().ResourceQuotas(namespace).Get(name)
if err != nil {
log.Printf("[DEBUG] Received error: %#v", err)
return err
}
log.Printf("[INFO] Received resource quota: %#v", resQuota)
// This is to work around K8S bug
// See https://github.com/kubernetes/kubernetes/issues/44539
if resQuota.ObjectMeta.GenerateName == "" {
if v, ok := d.GetOk("metadata.0.generate_name"); ok {
resQuota.ObjectMeta.GenerateName = v.(string)
}
}
err = d.Set("metadata", flattenMetadata(resQuota.ObjectMeta))
if err != nil {
return err
}
err = d.Set("spec", flattenResourceQuotaSpec(resQuota.Spec))
if err != nil {
return err
}
return nil
}
func resourceKubernetesResourceQuotaUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*kubernetes.Clientset)
namespace, name := idParts(d.Id())
ops := patchMetadata("metadata.0.", "/metadata/", d)
var spec api.ResourceQuotaSpec
waitForChangedSpec := false
if d.HasChange("spec") {
var err error
spec, err = expandResourceQuotaSpec(d.Get("spec").([]interface{}))
if err != nil {
return err
}
ops = append(ops, &ReplaceOperation{
Path: "/spec",
Value: spec,
})
waitForChangedSpec = true
}
data, err := ops.MarshalJSON()
if err != nil {
return fmt.Errorf("Failed to marshal update operations: %s", err)
}
log.Printf("[INFO] Updating resource quota %q: %v", name, string(data))
out, err := conn.CoreV1().ResourceQuotas(namespace).Patch(name, pkgApi.JSONPatchType, data)
if err != nil {
return fmt.Errorf("Failed to update resource quota: %s", err)
}
log.Printf("[INFO] Submitted updated resource quota: %#v", out)
d.SetId(buildId(out.ObjectMeta))
if waitForChangedSpec {
err = resource.Retry(1*time.Minute, func() *resource.RetryError {
quota, err := conn.CoreV1().ResourceQuotas(namespace).Get(name)
if err != nil {
return resource.NonRetryableError(err)
}
if resourceListEquals(spec.Hard, quota.Status.Hard) {
return nil
}
err = fmt.Errorf("Quotas don't match after update.\nExpected: %#v\nGiven: %#v",
spec.Hard, quota.Status.Hard)
return resource.RetryableError(err)
})
if err != nil {
return err
}
}
return resourceKubernetesResourceQuotaRead(d, meta)
}
func resourceKubernetesResourceQuotaDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*kubernetes.Clientset)
namespace, name := idParts(d.Id())
log.Printf("[INFO] Deleting resource quota: %#v", name)
err := conn.CoreV1().ResourceQuotas(namespace).Delete(name, &api.DeleteOptions{})
if err != nil {
return err
}
log.Printf("[INFO] Resource quota %s deleted", name)
d.SetId("")
return nil
}
func resourceKubernetesResourceQuotaExists(d *schema.ResourceData, meta interface{}) (bool, error) {
conn := meta.(*kubernetes.Clientset)
namespace, name := idParts(d.Id())
log.Printf("[INFO] Checking resource quota %s", name)
_, err := conn.CoreV1().ResourceQuotas(namespace).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)
}
return true, err
}

View File

@ -0,0 +1,352 @@
package kubernetes
import (
"fmt"
"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 TestAccKubernetesResourceQuota_basic(t *testing.T) {
var conf api.ResourceQuota
name := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(10))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: "kubernetes_resource_quota.test",
Providers: testAccProviders,
CheckDestroy: testAccCheckKubernetesResourceQuotaDestroy,
Steps: []resource.TestStep{
{
Config: testAccKubernetesResourceQuotaConfig_basic(name),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesResourceQuotaExists("kubernetes_resource_quota.test", &conf),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.annotations.%", "1"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.annotations.TestAnnotationOne", "one"),
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{"TestAnnotationOne": "one"}),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.labels.%", "3"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.labels.TestLabelOne", "one"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.labels.TestLabelThree", "three"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.labels.TestLabelFour", "four"),
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{"TestLabelOne": "one", "TestLabelThree": "three", "TestLabelFour": "four"}),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.name", name),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.self_link"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.%", "3"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.limits.cpu", "2"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.limits.memory", "2Gi"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.pods", "4"),
),
},
{
Config: testAccKubernetesResourceQuotaConfig_metaModified(name),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesResourceQuotaExists("kubernetes_resource_quota.test", &conf),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.annotations.%", "2"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.annotations.TestAnnotationOne", "one"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.annotations.TestAnnotationTwo", "two"),
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{"TestAnnotationOne": "one", "TestAnnotationTwo": "two"}),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.labels.%", "3"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.labels.TestLabelOne", "one"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.labels.TestLabelTwo", "two"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.labels.TestLabelThree", "three"),
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{"TestLabelOne": "one", "TestLabelTwo": "two", "TestLabelThree": "three"}),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.name", name),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.self_link"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.%", "3"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.limits.cpu", "2"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.limits.memory", "2Gi"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.pods", "4"),
),
},
{
Config: testAccKubernetesResourceQuotaConfig_specModified(name),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesResourceQuotaExists("kubernetes_resource_quota.test", &conf),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.annotations.%", "0"),
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.labels.%", "0"),
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.name", name),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.self_link"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.%", "4"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.limits.cpu", "4"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.requests.cpu", "1"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.limits.memory", "4Gi"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.pods", "10"),
),
},
},
})
}
func TestAccKubernetesResourceQuota_generatedName(t *testing.T) {
var conf api.ResourceQuota
prefix := "tf-acc-test-"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: "kubernetes_resource_quota.test",
Providers: testAccProviders,
CheckDestroy: testAccCheckKubernetesResourceQuotaDestroy,
Steps: []resource.TestStep{
{
Config: testAccKubernetesResourceQuotaConfig_generatedName(prefix),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesResourceQuotaExists("kubernetes_resource_quota.test", &conf),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.annotations.%", "0"),
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.labels.%", "0"),
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.generate_name", prefix),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.self_link"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.%", "1"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.pods", "10"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.scopes.#", "0"),
),
},
},
})
}
func TestAccKubernetesResourceQuota_withScopes(t *testing.T) {
var conf api.ResourceQuota
name := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(10))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: "kubernetes_resource_quota.test",
Providers: testAccProviders,
CheckDestroy: testAccCheckKubernetesResourceQuotaDestroy,
Steps: []resource.TestStep{
{
Config: testAccKubernetesResourceQuotaConfig_withScopes(name),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesResourceQuotaExists("kubernetes_resource_quota.test", &conf),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.annotations.%", "0"),
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.labels.%", "0"),
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.name", name),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.self_link"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.%", "1"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.pods", "10"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.scopes.#", "1"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.scopes.193563370", "BestEffort"),
),
},
{
Config: testAccKubernetesResourceQuotaConfig_withScopesModified(name),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesResourceQuotaExists("kubernetes_resource_quota.test", &conf),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.annotations.%", "0"),
testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.labels.%", "0"),
testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{}),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "metadata.0.name", name),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.self_link"),
resource.TestCheckResourceAttrSet("kubernetes_resource_quota.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.%", "1"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.hard.pods", "10"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.scopes.#", "1"),
resource.TestCheckResourceAttr("kubernetes_resource_quota.test", "spec.0.scopes.3022121741", "NotBestEffort"),
),
},
},
})
}
func TestAccKubernetesResourceQuota_importBasic(t *testing.T) {
resourceName := "kubernetes_resource_quota.test"
name := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(10))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckKubernetesResourceQuotaDestroy,
Steps: []resource.TestStep{
{
Config: testAccKubernetesResourceQuotaConfig_basic(name),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func testAccCheckKubernetesResourceQuotaDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*kubernetes.Clientset)
for _, rs := range s.RootModule().Resources {
if rs.Type != "kubernetes_resource_quota" {
continue
}
namespace, name := idParts(rs.Primary.ID)
resp, err := conn.CoreV1().ResourceQuotas(namespace).Get(name)
if err == nil {
if resp.Namespace == namespace && resp.Name == name {
return fmt.Errorf("Resource Quota still exists: %s", rs.Primary.ID)
}
}
}
return nil
}
func testAccCheckKubernetesResourceQuotaExists(n string, obj *api.ResourceQuota) 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)
namespace, name := idParts(rs.Primary.ID)
out, err := conn.CoreV1().ResourceQuotas(namespace).Get(name)
if err != nil {
return err
}
*obj = *out
return nil
}
}
func testAccKubernetesResourceQuotaConfig_basic(name string) string {
return fmt.Sprintf(`
resource "kubernetes_resource_quota" "test" {
metadata {
annotations {
TestAnnotationOne = "one"
}
labels {
TestLabelOne = "one"
TestLabelThree = "three"
TestLabelFour = "four"
}
name = "%s"
}
spec {
hard {
"limits.cpu" = 2
"limits.memory" = "2Gi"
pods = 4
}
}
}
`, name)
}
func testAccKubernetesResourceQuotaConfig_metaModified(name string) string {
return fmt.Sprintf(`
resource "kubernetes_resource_quota" "test" {
metadata {
annotations {
TestAnnotationOne = "one"
TestAnnotationTwo = "two"
}
labels {
TestLabelOne = "one"
TestLabelTwo = "two"
TestLabelThree = "three"
}
name = "%s"
}
spec {
hard {
"limits.cpu" = 2
"limits.memory" = "2Gi"
pods = 4
}
}
}
`, name)
}
func testAccKubernetesResourceQuotaConfig_specModified(name string) string {
return fmt.Sprintf(`
resource "kubernetes_resource_quota" "test" {
metadata {
name = "%s"
}
spec {
hard {
"limits.cpu" = 4
"requests.cpu" = 1
"limits.memory" = "4Gi"
pods = 10
}
}
}
`, name)
}
func testAccKubernetesResourceQuotaConfig_generatedName(prefix string) string {
return fmt.Sprintf(`
resource "kubernetes_resource_quota" "test" {
metadata {
generate_name = "%s"
}
spec {
hard {
pods = 10
}
}
}
`, prefix)
}
func testAccKubernetesResourceQuotaConfig_withScopes(name string) string {
return fmt.Sprintf(`
resource "kubernetes_resource_quota" "test" {
metadata {
name = "%s"
}
spec {
hard {
pods = 10
}
scopes = ["BestEffort"]
}
}
`, name)
}
func testAccKubernetesResourceQuotaConfig_withScopesModified(name string) string {
return fmt.Sprintf(`
resource "kubernetes_resource_quota" "test" {
metadata {
name = "%s"
}
spec {
hard {
pods = 10
}
scopes = ["NotBestEffort"]
}
}
`, name)
}

View File

@ -163,12 +163,22 @@ func flattenResourceList(l api.ResourceList) map[string]string {
func expandMapToResourceList(m map[string]interface{}) (api.ResourceList, error) { func expandMapToResourceList(m map[string]interface{}) (api.ResourceList, error) {
out := make(map[api.ResourceName]resource.Quantity) out := make(map[api.ResourceName]resource.Quantity)
for stringKey, v := range m { for stringKey, origValue := range m {
key := api.ResourceName(stringKey) key := api.ResourceName(stringKey)
value, err := resource.ParseQuantity(v.(string)) var value resource.Quantity
if v, ok := origValue.(int); ok {
q := resource.NewQuantity(int64(v), resource.DecimalExponent)
value = *q
} else if v, ok := origValue.(string); ok {
var err error
value, err = resource.ParseQuantity(v)
if err != nil { if err != nil {
return out, err return out, err
} }
} else {
return out, fmt.Errorf("Unexpected value type: %#v", origValue)
}
out[key] = value out[key] = value
} }
@ -191,6 +201,55 @@ func expandPersistentVolumeAccessModes(s []interface{}) []api.PersistentVolumeAc
return out return out
} }
func flattenResourceQuotaSpec(in api.ResourceQuotaSpec) []interface{} {
out := make([]interface{}, 1)
m := make(map[string]interface{}, 0)
m["hard"] = flattenResourceList(in.Hard)
m["scopes"] = flattenResourceQuotaScopes(in.Scopes)
out[0] = m
return out
}
func expandResourceQuotaSpec(s []interface{}) (api.ResourceQuotaSpec, error) {
out := api.ResourceQuotaSpec{}
if len(s) < 1 {
return out, nil
}
m := s[0].(map[string]interface{})
if v, ok := m["hard"]; ok {
list, err := expandMapToResourceList(v.(map[string]interface{}))
if err != nil {
return out, err
}
out.Hard = list
}
if v, ok := m["scopes"]; ok {
out.Scopes = expandResourceQuotaScopes(v.(*schema.Set).List())
}
return out, nil
}
func flattenResourceQuotaScopes(in []api.ResourceQuotaScope) *schema.Set {
out := make([]string, len(in), len(in))
for i, scope := range in {
out[i] = string(scope)
}
return newStringSet(schema.HashString, out)
}
func expandResourceQuotaScopes(s []interface{}) []api.ResourceQuotaScope {
out := make([]api.ResourceQuotaScope, len(s), len(s))
for i, scope := range s {
out[i] = api.ResourceQuotaScope(scope.(string))
}
return out
}
func newStringSet(f schema.SchemaSetFunc, in []string) *schema.Set { func newStringSet(f schema.SchemaSetFunc, in []string) *schema.Set {
var out = make([]interface{}, len(in), len(in)) var out = make([]interface{}, len(in), len(in))
for i, v := range in { for i, v := range in {
@ -198,3 +257,25 @@ func newStringSet(f schema.SchemaSetFunc, in []string) *schema.Set {
} }
return schema.NewSet(f, out) return schema.NewSet(f, out)
} }
func resourceListEquals(x, y api.ResourceList) bool {
for k, v := range x {
yValue, ok := y[k]
if !ok {
return false
}
if v.Cmp(yValue) != 0 {
return false
}
}
for k, v := range y {
xValue, ok := x[k]
if !ok {
return false
}
if v.Cmp(xValue) != 0 {
return false
}
}
return true
}

View File

@ -62,12 +62,21 @@ func validateLabels(value interface{}, key string) (ws []string, es []error) {
func validateResourceList(value interface{}, key string) (ws []string, es []error) { func validateResourceList(value interface{}, key string) (ws []string, es []error) {
m := value.(map[string]interface{}) m := value.(map[string]interface{})
for k, v := range m { for k, value := range m {
val := v.(string) if _, ok := value.(int); ok {
_, err := resource.ParseQuantity(val) continue
if err != nil {
es = append(es, fmt.Errorf("%s.%s (%q): %s", key, k, val, err))
} }
if v, ok := value.(string); ok {
_, err := resource.ParseQuantity(v)
if err != nil {
es = append(es, fmt.Errorf("%s.%s (%q): %s", key, k, v, err))
}
continue
}
err := "Value can be either string or int"
es = append(es, fmt.Errorf("%s.%s (%#v): %s", key, k, value, err))
} }
return return
} }

View File

@ -0,0 +1,69 @@
---
layout: "kubernetes"
page_title: "Kubernetes: kubernetes_resource_quota"
sidebar_current: "docs-kubernetes-resource-resource-quota"
description: |-
A resource quota provides constraints that limit aggregate resource consumption per namespace. It can limit the quantity of objects that can be created in a namespace by type, as well as the total amount of compute resources that may be consumed by resources in that project.
---
# kubernetes_resource_quota
A resource quota provides constraints that limit aggregate resource consumption per namespace. It can limit the quantity of objects that can be created in a namespace by type, as well as the total amount of compute resources that may be consumed by resources in that project.
## Example Usage
```hcl
resource "kubernetes_resource_quota" "example" {
metadata {
name = "terraform-example"
}
spec {
hard {
pods = 10
}
scopes = ["BestEffort"]
}
}
```
## Argument Reference
The following arguments are supported:
* `metadata` - (Required) Standard resource quota's metadata. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#metadata
* `spec` - (Optional) Spec defines the desired quota. http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
## Nested Blocks
### `metadata`
#### Arguments
* `annotations` - (Optional) An unstructured key value map stored with the resource quota that may be used to store arbitrary metadata. More info: http://kubernetes.io/docs/user-guide/annotations
* `labels` - (Optional) Map of string keys and values that can be used to organize and categorize (scope and select) the resource quota. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels
* `name` - (Optional) Name of the resource quota, must be unique. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names
* `namespace` - (Optional) Namespace defines the space within which name of the resource quota must be unique.
#### 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 resource quota that can be used by clients to determine when resource quota has changed. Read more: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#concurrency-control-and-consistency
* `self_link` - A URL representing this resource quota.
* `uid` - The unique in time and space value for this resource quota. More info: http://kubernetes.io/docs/user-guide/identifiers#uids
### `spec`
#### Arguments
* `hard` - (Optional) 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
* `scopes` - (Optional) A collection of filters that must match each object tracked by a quota. If not specified, the quota matches all objects.
## Import
Resource Quota can be imported using its name, e.g.
```
$ terraform import kubernetes_resource_quota.example terraform-example
```

View File

@ -25,6 +25,9 @@
<li<%= sidebar_current("docs-kubernetes-resource-persistent-volume-claim") %>> <li<%= sidebar_current("docs-kubernetes-resource-persistent-volume-claim") %>>
<a href="/docs/providers/kubernetes/r/persistent_volume_claim.html">kubernetes_persistent_volume_claim</a> <a href="/docs/providers/kubernetes/r/persistent_volume_claim.html">kubernetes_persistent_volume_claim</a>
</li> </li>
<li<%= sidebar_current("docs-kubernetes-resource-resource-quota") %>>
<a href="/docs/providers/kubernetes/r/resource_quota.html">kubernetes_resource_quota</a>
</li>
<li<%= sidebar_current("docs-kubernetes-resource-secret") %>> <li<%= sidebar_current("docs-kubernetes-resource-secret") %>>
<a href="/docs/providers/kubernetes/r/secret.html">kubernetes_secret</a> <a href="/docs/providers/kubernetes/r/secret.html">kubernetes_secret</a>
</li> </li>