Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provider/kubernetes: Add support for persistent_volume_claim #13527

Merged
merged 1 commit into from
Apr 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions builtin/providers/kubernetes/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,11 @@ func Provider() terraform.ResourceProvider {
},

ResourcesMap: map[string]*schema.Resource{
"kubernetes_config_map": resourceKubernetesConfigMap(),
"kubernetes_namespace": resourceKubernetesNamespace(),
"kubernetes_persistent_volume": resourceKubernetesPersistentVolume(),
"kubernetes_secret": resourceKubernetesSecret(),
"kubernetes_config_map": resourceKubernetesConfigMap(),
"kubernetes_namespace": resourceKubernetesNamespace(),
"kubernetes_persistent_volume": resourceKubernetesPersistentVolume(),
"kubernetes_persistent_volume_claim": resourceKubernetesPersistentVolumeClaim(),
"kubernetes_secret": resourceKubernetesSecret(),
},
ConfigureFunc: providerConfigure,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
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 resourceKubernetesPersistentVolumeClaim() *schema.Resource {
return &schema.Resource{
Create: resourceKubernetesPersistentVolumeClaimCreate,
Read: resourceKubernetesPersistentVolumeClaimRead,
Exists: resourceKubernetesPersistentVolumeClaimExists,
Update: resourceKubernetesPersistentVolumeClaimUpdate,
Delete: resourceKubernetesPersistentVolumeClaimDelete,
Importer: &schema.ResourceImporter{
State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
d.Set("wait_until_bound", true)
return []*schema.ResourceData{d}, nil
},
},

Timeouts: &schema.ResourceTimeout{
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,
},
},
},
},
"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,
},
},
}
}

func resourceKubernetesPersistentVolumeClaimCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*kubernetes.Clientset)

metadata := expandMetadata(d.Get("metadata").([]interface{}))
spec, err := expandPersistentVolumeClaimSpec(d.Get("spec").([]interface{}))
if err != nil {
return err
}

claim := api.PersistentVolumeClaim{
ObjectMeta: metadata,
Spec: spec,
}

log.Printf("[INFO] Creating new persistent volume claim: %#v", claim)
out, err := conn.CoreV1().PersistentVolumeClaims(metadata.Namespace).Create(&claim)
if err != nil {
return err
}
log.Printf("[INFO] Submitted new persistent volume claim: %#v", out)

d.SetId(buildId(out.ObjectMeta))
name := out.ObjectMeta.Name

if d.Get("wait_until_bound").(bool) {
stateConf := &resource.StateChangeConf{
Target: []string{"Bound"},
Pending: []string{"Pending"},
Timeout: d.Timeout(schema.TimeoutCreate),
Refresh: func() (interface{}, string, error) {
out, err := conn.CoreV1().PersistentVolumeClaims(metadata.Namespace).Get(name)
if err != nil {
log.Printf("[ERROR] Received error: %#v", err)
return out, "", err
}

statusPhase := fmt.Sprintf("%v", out.Status.Phase)
log.Printf("[DEBUG] Persistent volume claim %s status received: %#v", out.Name, statusPhase)
return out, statusPhase, nil
},
}
_, err = stateConf.WaitForState()
if err != nil {
return err
}
}
log.Printf("[INFO] Persistent volume claim %s created", out.Name)

return resourceKubernetesPersistentVolumeClaimRead(d, meta)
}

func resourceKubernetesPersistentVolumeClaimRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*kubernetes.Clientset)

namespace, name := idParts(d.Id())
log.Printf("[INFO] Reading persistent volume claim %s", name)
claim, err := conn.CoreV1().PersistentVolumeClaims(namespace).Get(name)
if err != nil {
log.Printf("[DEBUG] Received error: %#v", err)
return err
}
log.Printf("[INFO] Received persistent volume claim: %#v", claim)
err = d.Set("metadata", flattenMetadata(claim.ObjectMeta))
if err != nil {
return err
}
err = d.Set("spec", flattenPersistentVolumeClaimSpec(claim.Spec))
if err != nil {
return err
}

return nil
}

func resourceKubernetesPersistentVolumeClaimUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*kubernetes.Clientset)
namespace, name := idParts(d.Id())

ops := patchMetadata("metadata.0.", "/metadata/", d)
// The whole spec is ForceNew = nothing to update there
data, err := ops.MarshalJSON()
if err != nil {
return fmt.Errorf("Failed to marshal update operations: %s", err)
}

log.Printf("[INFO] Updating persistent volume claim: %s", ops)
out, err := conn.CoreV1().PersistentVolumeClaims(namespace).Patch(name, pkgApi.JSONPatchType, data)
if err != nil {
return err
}
log.Printf("[INFO] Submitted updated persistent volume claim: %#v", out)

return resourceKubernetesPersistentVolumeClaimRead(d, meta)
}

func resourceKubernetesPersistentVolumeClaimDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*kubernetes.Clientset)

namespace, name := idParts(d.Id())
log.Printf("[INFO] Deleting persistent volume claim: %#v", name)
err := conn.CoreV1().PersistentVolumeClaims(namespace).Delete(name, &api.DeleteOptions{})
if err != nil {
return err
}

log.Printf("[INFO] Persistent volume claim %s deleted", name)

d.SetId("")
return nil
}

func resourceKubernetesPersistentVolumeClaimExists(d *schema.ResourceData, meta interface{}) (bool, error) {
conn := meta.(*kubernetes.Clientset)

namespace, name := idParts(d.Id())
log.Printf("[INFO] Checking persistent volume claim %s", name)
_, err := conn.CoreV1().PersistentVolumeClaims(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
}
Loading