From 290c4a34934c2c544defa152aab17af54cd90423 Mon Sep 17 00:00:00 2001 From: Matt Morrison Date: Tue, 20 Feb 2018 11:35:06 +1300 Subject: [PATCH] BREAKING - Update DaemonSet schema to match API. --- kubernetes/resource_kubernetes_daemonset.go | 110 ++++++++++++++++++- kubernetes/resource_kubernetes_deployment.go | 3 +- kubernetes/structures_daemonset.go | 50 +++++++-- 3 files changed, 147 insertions(+), 16 deletions(-) diff --git a/kubernetes/resource_kubernetes_daemonset.go b/kubernetes/resource_kubernetes_daemonset.go index 7d95f41318..9c81a9934e 100755 --- a/kubernetes/resource_kubernetes_daemonset.go +++ b/kubernetes/resource_kubernetes_daemonset.go @@ -3,9 +3,11 @@ package kubernetes import ( "fmt" "log" + "strings" "time" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" pkgApi "k8s.io/apimachinery/pkg/types" @@ -23,6 +25,8 @@ func resourceKubernetesDaemonSet() *schema.Resource { Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, + SchemaVersion: 1, + MigrateState: resourceKubernetesDaemonSetStateUpgrader, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), @@ -92,11 +96,39 @@ func resourceKubernetesDaemonSet() *schema.Resource { }, "template": { Type: schema.TypeList, - Description: "Describes the pod that will be created if insufficient replicas are detected. This takes precedence over a TemplateRef. More info: http://kubernetes.io/docs/user-guide/replication-controller#pod-template", + Description: "Template describes the pods that will be created.", Required: true, MaxItems: 1, Elem: &schema.Resource{ - Schema: podSpecFields(false), + Schema: map[string]*schema.Schema{ + "metadata": metadataSchema("daemonsetSpec", true), + "spec": &schema.Schema{ + Type: schema.TypeList, + Description: "Spec describes the pods that will be created.", + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: podSpecFields(false), + }, + }, + "active_deadline_seconds": relocatedAttribute("active_deadline_seconds"), + "container": relocatedAttribute("container"), + "dns_policy": relocatedAttribute("dns_policy"), + "host_ipc": relocatedAttribute("host_ipc"), + "host_network": relocatedAttribute("host_network"), + "host_pid": relocatedAttribute("host_pid"), + "hostname": relocatedAttribute("hostname"), + "init_container": relocatedAttribute("init_container"), + "node_name": relocatedAttribute("node_name"), + "node_selector": relocatedAttribute("node_selector"), + "restart_policy": relocatedAttribute("restart_policy"), + "security_context": relocatedAttribute("security_context"), + "service_account_name": relocatedAttribute("service_account_name"), + "automount_service_account_token": relocatedAttribute("automount_service_account_token"), + "subdomain": relocatedAttribute("subdomain"), + "termination_grace_period_seconds": relocatedAttribute("termination_grace_period_seconds"), + "volume": relocatedAttribute("volume"), + }, }, }, }, @@ -114,7 +146,9 @@ func resourceKubernetesDaemonSetCreate(d *schema.ResourceData, meta interface{}) if err != nil { return err } - spec.Template.ObjectMeta.Annotations = metadata.Annotations + if metadata.Namespace == "" { + metadata.Namespace = "default" + } daemonset := v1beta1.DaemonSet{ ObjectMeta: metadata, @@ -151,7 +185,7 @@ func resourceKubernetesDaemonSetRead(d *schema.ResourceData, meta interface{}) e return err } - spec, err := flattenDaemonSetSpec(daemonset.Spec) + spec, err := flattenDaemonSetSpec(daemonset.Spec, d) if err != nil { return err } @@ -228,3 +262,71 @@ func resourceKubernetesDaemonSetExists(d *schema.ResourceData, meta interface{}) } return true, err } + +func resourceKubernetesDaemonSetStateUpgrader( + v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { + if is.Empty() { + log.Println("[DEBUG] Empty InstanceState; nothing to migrate.") + return is, nil + } + + var err error + + switch v { + case 0: + log.Println("[INFO] Found Kubernetes DaemonSet State v0; migrating to v1") + is, err = migrateDaemonSetStateV0toV1(is) + if err != nil { + return is, err + } + + default: + return is, fmt.Errorf("Unexpected schema version: %d", v) + } + + return is, err +} + +// This deployment resource originally had the podSpec directly below spec.template level +// This migration moves the state to spec.template.spec match the Kubernetes documented structure +func migrateDaemonSetStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) { + log.Printf("[DEBUG] Attributes before migration: %#v", is.Attributes) + + newTemplate := make(map[string]string) + + for k, v := range is.Attributes { + log.Println("[DEBUG] - checking attribute for state upgrade: ", k, v) + if strings.HasPrefix(k, "name") { + // don't clobber an existing metadata.0.name value + if _, ok := is.Attributes["metadata.0.name"]; ok { + continue + } + + newK := "metadata.0.name" + + newTemplate[newK] = v + log.Printf("[DEBUG] moved attribute %s -> %s ", k, newK) + delete(is.Attributes, k) + + } else if !strings.HasPrefix(k, "spec.0.template") { + continue + + } else if strings.HasPrefix(k, "spec.0.template.0.spec") || strings.HasPrefix(k, "spec.0.template.0.metadata") { + continue + + } else { + newK := strings.Replace(k, "spec.0.template.0", "spec.0.template.0.spec.0", 1) + + newTemplate[newK] = v + log.Printf("[DEBUG] moved attribute %s -> %s ", k, newK) + delete(is.Attributes, k) + } + } + + for k, v := range newTemplate { + is.Attributes[k] = v + } + + log.Printf("[DEBUG] Attributes after migration: %#v", is.Attributes) + return is, nil +} diff --git a/kubernetes/resource_kubernetes_deployment.go b/kubernetes/resource_kubernetes_deployment.go index 45c6cc447b..978f2c84f9 100755 --- a/kubernetes/resource_kubernetes_deployment.go +++ b/kubernetes/resource_kubernetes_deployment.go @@ -131,6 +131,7 @@ func resourceKubernetesDeployment() *schema.Resource { "host_network": relocatedAttribute("host_network"), "host_pid": relocatedAttribute("host_pid"), "hostname": relocatedAttribute("hostname"), + "init_container": relocatedAttribute("init_container"), "node_name": relocatedAttribute("node_name"), "node_selector": relocatedAttribute("node_selector"), "restart_policy": relocatedAttribute("restart_policy"), @@ -154,7 +155,7 @@ func relocatedAttribute(name string) *schema.Schema { s := &schema.Schema{ Type: schema.TypeString, Optional: true, - Removed: fmt.Sprintf("%s has been relocated to deployment/spec/template/spec/%s. Please update your Terraform config.", name, name), + Removed: fmt.Sprintf("%s has been relocated to [resource]/spec/template/spec/%s. Please update your Terraform config.", name, name), } return s } diff --git a/kubernetes/structures_daemonset.go b/kubernetes/structures_daemonset.go index 35a6037782..980cd57d3e 100755 --- a/kubernetes/structures_daemonset.go +++ b/kubernetes/structures_daemonset.go @@ -3,23 +3,34 @@ package kubernetes import ( "strconv" + "github.com/hashicorp/terraform/helper/schema" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/pkg/api/v1" "k8s.io/client-go/pkg/apis/extensions/v1beta1" ) -func flattenDaemonSetSpec(in v1beta1.DaemonSetSpec) ([]interface{}, error) { +func flattenDaemonSetSpec(in v1beta1.DaemonSetSpec, d *schema.ResourceData) ([]interface{}, error) { att := make(map[string]interface{}) att["min_ready_seconds"] = in.MinReadySeconds att["selector"] = in.Selector.MatchLabels att["strategy"] = flattenDaemonSetStrategy(in.UpdateStrategy) + // podSpec, err := flattenPodSpec(in.Template.Spec) + // if err != nil { + // return nil, err + // } + // att["template"] = podSpec + + templateMetadata := flattenMetadata(in.Template.ObjectMeta, d) podSpec, err := flattenPodSpec(in.Template.Spec) if err != nil { return nil, err } - att["template"] = podSpec + template := make(map[string]interface{}) + template["metadata"] = templateMetadata + template["spec"] = podSpec + att["template"] = []interface{}{template} return []interface{}{att}, nil } @@ -56,15 +67,32 @@ func expandDaemonSetSpec(deployment []interface{}) (v1beta1.DaemonSetSpec, error } } obj.UpdateStrategy = expandDaemonSetStrategy(in["strategy"].([]interface{})) - podSpec, err := expandPodSpec(in["template"].([]interface{})) - if err != nil { - return obj, err - } - obj.Template = v1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: obj.Selector.MatchLabels, - }, - Spec: podSpec, + + // podSpec, err := expandPodSpec(in["template"].([]interface{})) + // if err != nil { + // return obj, err + // } + // obj.Template = v1.PodTemplateSpec{ + // ObjectMeta: metav1.ObjectMeta{ + // Labels: obj.Selector.MatchLabels, + // }, + // Spec: podSpec, + // } + + for _, v := range in["template"].([]interface{}) { + template := v.(map[string]interface{}) + podSpec, err := expandPodSpec(template["spec"].([]interface{})) + if err != nil { + return obj, err + } + obj.Template = v1.PodTemplateSpec{ + Spec: podSpec, + } + + if metaCfg, ok := template["metadata"]; ok { + metadata := expandMetadata(metaCfg.([]interface{})) + obj.Template.ObjectMeta = metadata + } } return obj, nil