Skip to content

Commit

Permalink
Add volume_claim_templates to stateful_set
Browse files Browse the repository at this point in the history
  • Loading branch information
sl1pm4t committed Dec 14, 2017
1 parent b6a37a2 commit 2ddea94
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 112 deletions.
113 changes: 1 addition & 112 deletions kubernetes/resource_kubernetes_persistent_volume_claim.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,118 +32,7 @@ func resourceKubernetesPersistentVolumeClaim() *schema.Resource {
Create: schema.DefaultTimeout(5 * time.Minute),
},

Schema: map[string]*schema.Schema{
"metadata": namespacedMetadataSchema("persistent volume claim", true),
"spec": {
Type: schema.TypeList,
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",
Required: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"access_modes": {
Type: schema.TypeSet,
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",
Required: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"resources": {
Type: schema.TypeList,
Description: "A list of the minimum resources the volume should have. More info: http://kubernetes.io/docs/user-guide/persistent-volumes#resources",
Required: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"limits": {
Type: schema.TypeMap,
Description: "Map describing the maximum amount of compute resources allowed. More info: http://kubernetes.io/docs/user-guide/compute-resources/",
Optional: true,
ForceNew: true,
},
"requests": {
Type: schema.TypeMap,
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/",
Optional: true,
ForceNew: true,
},
},
},
},
"selector": {
Type: schema.TypeList,
Description: "A label query over volumes to consider for binding.",
Optional: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"match_expressions": {
Type: schema.TypeList,
Description: "A list of label selector requirements. The requirements are ANDed.",
Optional: true,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Description: "The label key that the selector applies to.",
Optional: true,
ForceNew: true,
},
"operator": {
Type: schema.TypeString,
Description: "A key's relationship to a set of values. Valid operators ard `In`, `NotIn`, `Exists` and `DoesNotExist`.",
Optional: true,
ForceNew: true,
},
"values": {
Type: schema.TypeSet,
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.",
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
},
},
"match_labels": {
Type: schema.TypeMap,
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.",
Optional: true,
ForceNew: true,
},
},
},
},
"volume_name": {
Type: schema.TypeString,
Description: "The binding reference to the PersistentVolume backing this claim.",
Optional: true,
ForceNew: true,
Computed: true,
},
"storage_class_name": {
Type: schema.TypeString,
Description: "Name of the storage class requested by the claim",
Optional: true,
Computed: true,
ForceNew: true,
},
},
},
},
"wait_until_bound": {
Type: schema.TypeBool,
Description: "Whether to wait for the claim to reach `Bound` state (to find volume in which to claim the space)",
Optional: true,
Default: true,
},
},
Schema: persistentVolumeClaimSpecFields(false),
}
}

Expand Down
8 changes: 8 additions & 0 deletions kubernetes/resource_kubernetes_stateful_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ func resourceKubernetesStatefulSet() *schema.Resource {
Schema: podSpecFields(true),
},
},
"volume_claim_templates": {
Type: schema.TypeList,
Optional: true,
Description: "volumeClaimTemplates is a list of claims that pods are allowed to reference. The StatefulSet controller is responsible for mapping network identities to claims in a way that maintains the identity of a pod. Every claim in this list must have at least one matching (by name) volumeMount in one container in the template. A claim in this list takes precedence over any volumes in the template, with the same name.",
Elem: &schema.Resource{
Schema: persistentVolumeClaimSpecFields(true),
},
},
},
},
},
Expand Down
76 changes: 76 additions & 0 deletions kubernetes/resource_kubernetes_stateful_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,39 @@ func TestAccKubernetesStatefulSet_basic(t *testing.T) {
})
}

func TestAccKubernetesStatefulSet_pvcTemplate(t *testing.T) {
var sset v1beta1.StatefulSet

statefulSetName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))

imageName1 := "nginx:1.7.9"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckKubernetesStatefulSetDestroy,
Steps: []resource.TestStep{
{
Config: testAccKubernetesStatefulSetConfig_pvcTemplate(statefulSetName, imageName1),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKubernetesStatefulSetExists("kubernetes_stateful_set.test", &sset),
resource.TestCheckResourceAttr("kubernetes_stateful_set.test", "metadata.0.name", statefulSetName),
resource.TestCheckResourceAttr("kubernetes_stateful_set.test", "metadata.0.labels.%", "1"),
resource.TestCheckResourceAttr("kubernetes_stateful_set.test", "metadata.0.labels.app", "one"),
resource.TestCheckResourceAttrSet("kubernetes_stateful_set.test", "metadata.0.generation"),
resource.TestCheckResourceAttrSet("kubernetes_stateful_set.test", "metadata.0.resource_version"),
resource.TestCheckResourceAttrSet("kubernetes_stateful_set.test", "metadata.0.self_link"),
resource.TestCheckResourceAttrSet("kubernetes_stateful_set.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_stateful_set.test", "spec.0.service_name", statefulSetName),
resource.TestCheckResourceAttr("kubernetes_stateful_set.test", "spec.0.template.0.container.0.image", imageName1),
resource.TestCheckResourceAttr("kubernetes_stateful_set.test", "spec.0.template.0.container.0.image", imageName1),
resource.TestCheckResourceAttr("kubernetes_stateful_set.test", "volume_claim_templates.#", "1"),
),
},
},
})
}

func testAccCheckKubernetesStatefulSetExists(n string, obj *v1beta1.StatefulSet) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
Expand Down Expand Up @@ -114,3 +147,46 @@ resource "kubernetes_stateful_set" "test" {
}
`, name, name, image)
}

func testAccKubernetesStatefulSetConfig_pvcTemplate(name, image string) string {
return fmt.Sprintf(`
resource "kubernetes_stateful_set" "test" {
metadata {
name = "%s"
labels {
app = "one"
}
}
spec {
replicas = 2
selector {
app = "one"
}
service_name = "%s"
template {
container {
image = "%s"
name = "tf-acc-test"
}
}
volume_claim_templates {
metadata {
name = "pvc"
annotations {
"volume.alpha.kubernetes.io/storage-class" = "anything"
}
}
spec {
access_modes = ["ReadWriteOnce"]
resources {
requests {
storage = "2Gi"
}
}
}
}
}
}
`, name, name, image)
}
120 changes: 120 additions & 0 deletions kubernetes/schema_persistent_volume_claim_spec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package kubernetes

import "github.com/hashicorp/terraform/helper/schema"

func persistentVolumeClaimSpecFields(pvcTemplate bool) map[string]*schema.Schema {
s := map[string]*schema.Schema{
"metadata": namespacedMetadataSchema("persistent volume claim", true),
"spec": {
Type: schema.TypeList,
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",
Required: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"access_modes": {
Type: schema.TypeSet,
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",
Required: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"resources": {
Type: schema.TypeList,
Description: "A list of the minimum resources the volume should have. More info: http://kubernetes.io/docs/user-guide/persistent-volumes#resources",
Required: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"limits": {
Type: schema.TypeMap,
Description: "Map describing the maximum amount of compute resources allowed. More info: http://kubernetes.io/docs/user-guide/compute-resources/",
Optional: true,
ForceNew: true,
},
"requests": {
Type: schema.TypeMap,
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/",
Optional: true,
ForceNew: true,
},
},
},
},
"selector": {
Type: schema.TypeList,
Description: "A label query over volumes to consider for binding.",
Optional: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"match_expressions": {
Type: schema.TypeList,
Description: "A list of label selector requirements. The requirements are ANDed.",
Optional: true,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Description: "The label key that the selector applies to.",
Optional: true,
ForceNew: true,
},
"operator": {
Type: schema.TypeString,
Description: "A key's relationship to a set of values. Valid operators ard `In`, `NotIn`, `Exists` and `DoesNotExist`.",
Optional: true,
ForceNew: true,
},
"values": {
Type: schema.TypeSet,
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.",
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
},
},
"match_labels": {
Type: schema.TypeMap,
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.",
Optional: true,
ForceNew: true,
},
},
},
},
"volume_name": {
Type: schema.TypeString,
Description: "The binding reference to the PersistentVolume backing this claim.",
Optional: true,
ForceNew: true,
Computed: true,
},
"storage_class_name": {
Type: schema.TypeString,
Description: "Name of the storage class requested by the claim",
Optional: true,
Computed: true,
ForceNew: true,
},
},
},
},
"wait_until_bound": {
Type: schema.TypeBool,
Description: "Whether to wait for the claim to reach `Bound` state (to find volume in which to claim the space)",
Optional: true,
Default: !pvcTemplate,
},
}

return s
}
18 changes: 18 additions & 0 deletions kubernetes/structures.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,24 @@ func flattenMetadata(meta metav1.ObjectMeta, d *schema.ResourceData) []map[strin
return []map[string]interface{}{m}
}

func flattenSubMetadata(meta metav1.ObjectMeta) []map[string]interface{} {
m := make(map[string]interface{})

m["annotations"] = meta.Annotations
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}
}

func removeInternalKeys(m map[string]string, d map[string]interface{}) map[string]string {
for k, _ := range m {
if isInternalKey(k) && !isKeyInMap(k, d) {
Expand Down
Loading

0 comments on commit 2ddea94

Please sign in to comment.