Skip to content

Instantly share code, notes, and snippets.

@lgfa29
Created July 11, 2023 00:18
Show Gist options
  • Save lgfa29/c2a0c588da8724c3fd916b3e97f98b4b to your computer and use it in GitHub Desktop.
Save lgfa29/c2a0c588da8724c3fd916b3e97f98b4b to your computer and use it in GitHub Desktop.
--- nomad/resource_external_volume.go 2023-07-10 19:54:22
+++ nomad/resource_csi_volume.go 2023-07-10 17:40:54
@@ -14,33 +14,20 @@
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+ "github.com/hashicorp/terraform-provider-nomad/nomad/helper"
)
-func resourceExternalVolume() *schema.Resource {
+func resourceCSIVolume() *schema.Resource {
return &schema.Resource{
- DeprecationMessage: "nomad_external_volume is deprecated and may be removed in a future release. Use nomad_csi_volume instead.",
+ Create: resourceCSIVolumeCreate,
+ Update: resourceCSIVolumeCreate,
+ Delete: resourceCSIVolumeDelete,
- Create: resourceExternalVolumeCreate,
- Update: resourceExternalVolumeCreate,
- Delete: resourceExternalVolumeDelete,
-
- // Once created, external volumes are automatically registered as a
+ // Once created, CSI volumes are automatically registered as a
// normal volume.
- Read: resourceVolumeRead,
+ Read: resourceCSIVolumeRead,
Schema: map[string]*schema.Schema{
- "type": {
- ForceNew: true,
- Type: schema.TypeString,
- Optional: true,
- Description: "The type of the volume. Currently, only 'csi' is supported.",
- Default: "csi",
- Elem: &schema.Schema{
- Type: schema.TypeString,
- ValidateFunc: validation.StringInSlice([]string{"csi"}, false),
- },
- },
-
"namespace": {
ForceNew: true,
Description: "The namespace in which to create the volume.",
@@ -318,7 +305,7 @@
}
}
-func resourceExternalVolumeCreate(d *schema.ResourceData, meta interface{}) error {
+func resourceCSIVolumeCreate(d *schema.ResourceData, meta interface{}) error {
providerConfig := meta.(ProviderConfig)
client := providerConfig.client
@@ -334,12 +321,12 @@
}
// Parse capabilities set.
- capabilities, err := parseVolumeCapabilities(d.Get("capability"))
+ capabilities, err := parseCSIVolumeCapabilities(d.Get("capability"))
if err != nil {
return fmt.Errorf("failed to unpack capabilities: %v", err)
}
- topologyRequest, err := parseVolumeTopologyRequest(d.Get("topology_request"))
+ topologyRequest, err := parseCSIVolumeTopologyRequest(d.Get("topology_request"))
if err != nil {
return fmt.Errorf("failed to unpack topology request: %v", err)
}
@@ -354,8 +341,8 @@
RequestedCapacityMax: int64(capacityMax),
RequestedCapabilities: capabilities,
RequestedTopologies: topologyRequest,
- Secrets: toMapStringString(d.Get("secrets")),
- Parameters: toMapStringString(d.Get("parameters")),
+ Secrets: helper.ToMapStringString(d.Get("secrets")),
+ Parameters: helper.ToMapStringString(d.Get("parameters")),
}
// Unpack the mount_options if we have any and configure the volume struct.
@@ -384,7 +371,7 @@
}
// Create the volume.
- log.Printf("[DEBUG] creating volume %q in namespace %q", volume.ID, volume.Namespace)
+ log.Printf("[DEBUG] creating CSI volume %q in namespace %q", volume.ID, volume.Namespace)
opts := &api.WriteOptions{
Namespace: d.Get("namespace").(string),
}
@@ -393,21 +380,21 @@
}
_, _, err = client.CSIVolumes().Create(volume, opts)
if err != nil {
- return fmt.Errorf("error creating volume: %s", err)
+ return fmt.Errorf("error creating CSI volume: %s", err)
}
- log.Printf("[DEBUG] volume %q created in namespace %q", volume.ID, volume.Namespace)
+ log.Printf("[DEBUG] CSI volume %q created in namespace %q", volume.ID, volume.Namespace)
d.SetId(volume.ID)
- return resourceVolumeRead(d, meta) // populate other computed attributes
+ return resourceCSIVolumeRead(d, meta) // populate other computed attributes
}
-func resourceExternalVolumeDelete(d *schema.ResourceData, meta interface{}) error {
+func resourceCSIVolumeDelete(d *schema.ResourceData, meta interface{}) error {
providerConfig := meta.(ProviderConfig)
client := providerConfig.client
id := d.Id()
- log.Printf("[DEBUG] deleting volume: %q", id)
+ log.Printf("[DEBUG] deleting CSI volume: %q", id)
opts := &api.WriteOptions{
Namespace: d.Get("namespace").(string),
}
@@ -416,7 +403,7 @@
}
err := client.CSIVolumes().Delete(id, opts)
if err != nil {
- return fmt.Errorf("error deleting volume: %s", err)
+ return fmt.Errorf("error deleting CSI volume: %s", err)
}
return nil
--- nomad/resource_volume.go 2023-07-10 19:53:59
+++ nomad/resource_csi_volume_registration.go 2023-07-10 18:48:18
@@ -5,7 +5,6 @@
import (
"bytes"
- "context"
"errors"
"fmt"
"hash/crc32"
@@ -15,35 +14,21 @@
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+ "github.com/hashicorp/terraform-provider-nomad/nomad/helper"
)
-func resourceVolume() *schema.Resource {
+func resourceCSIVolumeRegistration() *schema.Resource {
return &schema.Resource{
- DeprecationMessage: "nomad_volume is deprecated and may be removed in a future release. Use nomad_csi_volume_registration instead.",
+ Create: resourceCSIVolumeRegistrationCreate,
+ Update: resourceCSIVolumeRegistrationCreate,
+ Delete: resourceCSIVolumeRegistrationDelete,
+ Read: resourceCSIVolumeRead,
- Create: resourceVolumeCreate,
- Update: resourceVolumeCreate,
- Delete: resourceVolumeDelete,
- Read: resourceVolumeRead,
-
Schema: map[string]*schema.Schema{
// the following cannot be updated without destroying:
// - Namespace/ID
// - PluginID
// - ExternalID
- // - Type
-
- "type": {
- ForceNew: true,
- Type: schema.TypeString,
- Optional: true,
- Description: "The type of the volume. Currently, only 'csi' is supported.",
- Default: "csi",
- Elem: &schema.Schema{
- Type: schema.TypeString,
- ValidateFunc: validation.StringInSlice([]string{"csi"}, false),
- },
- },
"namespace": {
ForceNew: true,
@@ -82,13 +67,9 @@
"capability": {
Description: "Capabilities intended to be used in a job. At least one capability must be provided.",
- // COMPAT(1.5.0)
- // Update once `access_mode` and `attachment_mode` are removed.
- ForceNew: false,
- Optional: true,
- ConflictsWith: []string{"access_mode", "attachment_mode"},
- Type: schema.TypeSet,
- MinItems: 1,
+ Optional: true,
+ Type: schema.TypeSet,
+ MinItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"access_mode": {
@@ -214,7 +195,7 @@
},
},
// Volume registration does not support preferred topologies.
- // https://www.nomadproject.io/docs/commands/volume/register#topology_request-parameters
+ // https://developer.hashicorp.com/nomad/docs/other-specifications/volume/topology_request#preferred
},
},
},
@@ -287,95 +268,22 @@
},
},
},
- },
- },
-
- // COMPAT(1.5.0)
- // Deprecated fields.
- "access_mode": {
- Description: "Defines whether a volume should be available concurrently.",
- Optional: true,
- Deprecated: "use capability instead",
- ConflictsWith: []string{"capability"},
- Type: schema.TypeString,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- ValidateFunc: validation.StringInSlice([]string{
- "single-node-reader-only",
- "single-node-writer",
- "multi-node-reader-only",
- "multi-node-single-writer",
- "multi-node-multi-writer",
- }, false),
},
},
-
- "attachment_mode": {
- Description: "The storage API that will be used by the volume.",
- Optional: true,
- Deprecated: "use capability instead",
- ConflictsWith: []string{"capability"},
- Type: schema.TypeString,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- ValidateFunc: validation.StringInSlice([]string{
- "block-device",
- "file-system",
- }, false),
- },
- },
},
- SchemaVersion: 1,
- StateUpgraders: []schema.StateUpgrader{
- {
- Type: resourceVolumeResourceV0().CoreConfigSchema().ImpliedType(),
- Upgrade: resourceVolumeStateUpgradeV0,
- Version: 0,
- },
- },
}
}
-func toMapStringString(m interface{}) map[string]string {
- mss := map[string]string{}
- for k, v := range m.(map[string]interface{}) {
- mss[k] = v.(string)
- }
- return mss
-}
-
-func resourceVolumeCreate(d *schema.ResourceData, meta interface{}) error {
+func resourceCSIVolumeRegistrationCreate(d *schema.ResourceData, meta interface{}) error {
providerConfig := meta.(ProviderConfig)
client := providerConfig.client
- // Read capabilities maintaining backwards compatibility with the previous
- // fields attachment_mode and access_mode.
- capabilitySet, capabilitiesOk := d.GetOk("capability")
- attachmentMode, attachmentModeOk := d.GetOk("attachment_mode")
- accessMode, accessModeOk := d.GetOk("access_mode")
-
- if !capabilitiesOk && !attachmentModeOk && !accessModeOk {
- return errors.New(`one of "capability" or "access_mode" and "attachment_mode" must be configured`)
+ capabilities, err := parseCSIVolumeCapabilities(d.Get("capability"))
+ if err != nil {
+ return fmt.Errorf("failed to unpack capabilities: %v", err)
}
- var capabilities []*api.CSIVolumeCapability
-
- if capabilitiesOk {
- var err error
- capabilities, err = parseVolumeCapabilities(capabilitySet)
- if err != nil {
- return fmt.Errorf("failed to unpack capabilities: %v", err)
- }
- } else {
- capabilities = []*api.CSIVolumeCapability{
- {
- AccessMode: api.CSIVolumeAccessMode(accessMode.(string)),
- AttachmentMode: api.CSIVolumeAttachmentMode(attachmentMode.(string)),
- },
- }
- }
-
- topologyRequest, err := parseVolumeTopologyRequest(d.Get("topology_request"))
+ topologyRequest, err := parseCSIVolumeTopologyRequest(d.Get("topology_request"))
if err != nil {
return fmt.Errorf("failed to unpack topology request: %v", err)
}
@@ -386,9 +294,9 @@
ExternalID: d.Get("external_id").(string),
RequestedCapabilities: capabilities,
RequestedTopologies: topologyRequest,
- Secrets: toMapStringString(d.Get("secrets")),
- Parameters: toMapStringString(d.Get("parameters")),
- Context: toMapStringString(d.Get("context")),
+ Secrets: helper.ToMapStringString(d.Get("secrets")),
+ Parameters: helper.ToMapStringString(d.Get("parameters")),
+ Context: helper.ToMapStringString(d.Get("context")),
PluginID: d.Get("plugin_id").(string),
// COMPAT(1.5.0)
@@ -424,7 +332,7 @@
}
// Register the volume
- log.Printf("[DEBUG] registering volume %q in namespace %q", volume.ID, volume.Namespace)
+ log.Printf("[DEBUG] registering CSI volume %q in namespace %q", volume.ID, volume.Namespace)
opts := &api.WriteOptions{
Namespace: d.Get("namespace").(string),
}
@@ -433,16 +341,16 @@
}
_, err = client.CSIVolumes().Register(volume, opts)
if err != nil {
- return fmt.Errorf("error registering volume: %s", err)
+ return fmt.Errorf("error registering CSI volume: %s", err)
}
- log.Printf("[DEBUG] volume %q registered in namespace %q", volume.ID, volume.Namespace)
+ log.Printf("[DEBUG] CSI volume %q registered in namespace %q", volume.ID, volume.Namespace)
d.SetId(volume.ID)
- return resourceVolumeRead(d, meta) // populate other computed attributes
+ return resourceCSIVolumeRead(d, meta) // populate other computed attributes
}
-func resourceVolumeDelete(d *schema.ResourceData, meta interface{}) error {
+func resourceCSIVolumeRegistrationDelete(d *schema.ResourceData, meta interface{}) error {
providerConfig := meta.(ProviderConfig)
client := providerConfig.client
@@ -456,7 +364,7 @@
}
id := d.Id()
- log.Printf("[DEBUG] deregistering volume: %q", id)
+ log.Printf("[DEBUG] deregistering CSI volume: %q", id)
opts := &api.WriteOptions{
Namespace: d.Get("namespace").(string),
}
@@ -465,13 +373,13 @@
}
err := client.CSIVolumes().Deregister(id, true, opts)
if err != nil {
- return fmt.Errorf("error deregistering volume: %s", err)
+ return fmt.Errorf("error deregistering CSI volume: %s", err)
}
return nil
}
-func resourceVolumeRead(d *schema.ResourceData, meta interface{}) error {
+func resourceCSIVolumeRead(d *schema.ResourceData, meta interface{}) error {
providerConfig := meta.(ProviderConfig)
client := providerConfig.client
@@ -482,20 +390,20 @@
if opts.Namespace == "" {
opts.Namespace = "default"
}
- log.Printf("[DEBUG] reading information for volume %q in namespace %q", id, opts.Namespace)
+ log.Printf("[DEBUG] reading information for CSI volume %q in namespace %q", id, opts.Namespace)
volume, _, err := client.CSIVolumes().Info(id, opts)
if err != nil {
// As of Nomad 0.4.1, the API client returns an error for 404
// rather than a nil result, so we must check this way.
if strings.Contains(err.Error(), "404") {
- log.Printf("[DEBUG] volume %q does not exist, so removing", id)
+ log.Printf("[DEBUG] CSI volume %q does not exist, so removing", id)
d.SetId("")
return nil
}
- return fmt.Errorf("error checking for volume: %s", err)
+ return fmt.Errorf("error checking for CSI volume: %s", err)
}
- log.Printf("[DEBUG] found volume %q in namespace %q", volume.Name, volume.Namespace)
+ log.Printf("[DEBUG] found CSI volume %q in namespace %q", volume.Name, volume.Namespace)
d.Set("name", volume.Name)
d.Set("controller_required", volume.ControllerRequired)
@@ -507,14 +415,14 @@
d.Set("nodes_healthy", volume.NodesHealthy)
d.Set("nodes_expected", volume.NodesExpected)
d.Set("schedulable", volume.Schedulable)
- d.Set("topologies", flattenVolumeTopologies(volume.Topologies))
+ d.Set("topologies", flattenCSIVolumeTopologies(volume.Topologies))
// The Nomad API redacts `mount_options` and `secrets`, so we don't update them
// with the response payload; they will remain as is.
return nil
}
-func parseVolumeCapabilities(i interface{}) ([]*api.CSIVolumeCapability, error) {
+func parseCSIVolumeCapabilities(i interface{}) ([]*api.CSIVolumeCapability, error) {
capabilities := []*api.CSIVolumeCapability{}
capabilitySet, ok := i.(*schema.Set)
@@ -545,7 +453,7 @@
// parseVolumeTopologyRequest parses a Terraform state representation of volume
// topology request into its Nomad API representation.
-func parseVolumeTopologyRequest(i interface{}) (*api.CSITopologyRequest, error) {
+func parseCSIVolumeTopologyRequest(i interface{}) (*api.CSITopologyRequest, error) {
req := &api.CSITopologyRequest{}
var err error
@@ -564,14 +472,14 @@
}
if required, ok := topologyMap["required"]; ok {
- req.Required, err = parseVolumeTopologies("required", required)
+ req.Required, err = parseCSIVolumeTopologies("required", required)
if err != nil {
return nil, fmt.Errorf("failed to parse required CSI topology: %v", err)
}
}
if preferred, ok := topologyMap["preferred"]; ok {
- req.Preferred, err = parseVolumeTopologies("preferred", preferred)
+ req.Preferred, err = parseCSIVolumeTopologies("preferred", preferred)
if err != nil {
return nil, fmt.Errorf("failed to parse preferred CSI topology: %v", err)
}
@@ -582,7 +490,7 @@
// parseVolumeTopologis parses a Terraform state representation of volume
// topology into its Nomad API representation.
-func parseVolumeTopologies(prefix string, i interface{}) ([]*api.CSITopology, error) {
+func parseCSIVolumeTopologies(prefix string, i interface{}) ([]*api.CSITopology, error) {
var topologies []*api.CSITopology
topologiesList, ok := i.([]interface{})
@@ -632,7 +540,7 @@
// flattenVolumeTopologies turns a list of Nomad API CSITopology structs into
// the flat representation used by Terraform.
-func flattenVolumeTopologies(topologies []*api.CSITopology) []interface{} {
+func flattenCSIVolumeTopologies(topologies []*api.CSITopology) []interface{} {
topologiesList := []interface{}{}
for _, topo := range topologies {
@@ -645,188 +553,4 @@
}
return topologiesList
-}
-
-// resourceVolumeStateUpgradeV0 migrates a nomad_volume resource schema from v0 to v1.
-func resourceVolumeStateUpgradeV0(_ context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
- if val, ok := rawState["mount_options"]; ok {
- rawState["mount_options"] = []interface{}{val}
- }
- return rawState, nil
-}
-
-// resourceVolumeResourceV0 returns the v0 schema for a nomad_volume.
-func resourceVolumeResourceV0() *schema.Resource {
- return &schema.Resource{
- Schema: map[string]*schema.Schema{
- "type": {
- ForceNew: true,
- Type: schema.TypeString,
- Optional: true,
- Description: "The type of the volume. Currently, only 'csi' is supported.",
- Default: "csi",
- Elem: &schema.Schema{
- Type: schema.TypeString,
- ValidateFunc: validation.StringInSlice([]string{"csi"}, false),
- },
- },
-
- "namespace": {
- ForceNew: true,
- Description: "The namespace in which to create the volume.",
- Optional: true,
- Default: "default",
- Type: schema.TypeString,
- },
-
- "volume_id": {
- ForceNew: true,
- Description: "The unique ID of the volume, how jobs will refer to the volume.",
- Required: true,
- Type: schema.TypeString,
- },
-
- "name": {
- Description: "The display name of the volume.",
- Required: true,
- Type: schema.TypeString,
- },
-
- "plugin_id": {
- ForceNew: true,
- Description: "The ID of the CSI plugin that manages this volume.",
- Required: true,
- Type: schema.TypeString,
- },
-
- "external_id": {
- ForceNew: true,
- Description: "The ID of the physical volume from the storage provider.",
- Required: true,
- Type: schema.TypeString,
- },
-
- "access_mode": {
- Description: "Defines whether a volume should be available concurrently.",
- Required: true,
- Type: schema.TypeString,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- ValidateFunc: validation.StringInSlice([]string{
- "single-node-reader-only",
- "single-node-writer",
- "multi-node-reader-only",
- "multi-node-single-writer",
- "multi-node-multi-writer",
- }, false),
- },
- },
-
- "attachment_mode": {
- Description: "The storage API that will be used by the volume.",
- Required: true,
- Type: schema.TypeString,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- ValidateFunc: validation.StringInSlice([]string{
- "block-device",
- "file-system",
- }, false),
- },
- },
-
- "mount_options": {
- Description: "Options for mounting 'block-device' volumes without a pre-formatted file system.",
- Optional: true,
- Type: schema.TypeMap,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "fs_type": {
- Description: "The file system type.",
- Type: schema.TypeString,
- },
- "mount_flags": {
- Description: "The flags passed to mount.",
- Type: schema.TypeList,
- },
- },
- },
- },
-
- "secrets": {
- Description: "An optional key-value map of strings used as credentials for publishing and unpublishing volumes.",
- Optional: true,
- Type: schema.TypeMap,
- Sensitive: true,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- },
-
- "parameters": {
- Description: "An optional key-value map of strings passed directly to the CSI plugin to configure the volume.",
- Optional: true,
- Type: schema.TypeMap,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- },
-
- "context": {
- Description: "An optional key-value map of strings passed directly to the CSI plugin to validate the volume.",
- Optional: true,
- Type: schema.TypeMap,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- },
-
- "deregister_on_destroy": {
- Description: "If true, the volume will be deregistered on destroy.",
- Optional: true,
- Default: true,
- Type: schema.TypeBool,
- },
-
- "controller_required": {
- Computed: true,
- Type: schema.TypeBool,
- },
-
- "controllers_expected": {
- Computed: true,
- Type: schema.TypeInt,
- },
-
- "controllers_healthy": {
- Computed: true,
- Type: schema.TypeInt,
- },
-
- "plugin_provider": {
- Computed: true,
- Type: schema.TypeString,
- },
-
- "plugin_provider_version": {
- Computed: true,
- Type: schema.TypeString,
- },
-
- "nodes_healthy": {
- Computed: true,
- Type: schema.TypeInt,
- },
-
- "nodes_expected": {
- Computed: true,
- Type: schema.TypeInt,
- },
-
- "schedulable": {
- Computed: true,
- Type: schema.TypeBool,
- },
- },
- }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment