diff --git a/decort/data_source_rg.go b/decort/data_source_rg.go index 4599fec..c1ae000 100644 --- a/decort/data_source_rg.go +++ b/decort/data_source_rg.go @@ -55,7 +55,7 @@ func flattenResgroup(d *schema.ResourceData, rg_facts string) error { d.Set("name", details.Name) d.Set("account_name", details.AccountName) d.Set("account_id", details.AccountID) - d.Set("grid_id", details.GridID) + // d.Set("grid_id", details.GridID) d.Set("description", details.Desc) d.Set("status", details.Status) d.Set("def_net_type", details.DefaultNetType) @@ -111,14 +111,14 @@ func dataSourceResgroup() *schema.Resource { "account_name": { Type: schema.TypeString, - Optional: true, + Computed: true, Description: "Name of the account, which this resource group belongs to.", }, "account_id": { Type: schema.TypeInt, - Optional: true, - Description: "Unique ID of the account, which this resource group belongs to. If account ID is specified, then account name is ignored.", + Required: true, + Description: "Unique ID of the account, which this resource group belongs to.", }, "description": { @@ -127,11 +127,13 @@ func dataSourceResgroup() *schema.Resource { Description: "User-defined text description of this resource group.", }, + /* commented out, as in this version of provider we use default Grid ID "grid_id": { Type: schema.TypeInt, Computed: true, Description: "Unique ID of the grid, where this resource group is deployed.", }, + */ "quota": { Type: schema.TypeList, @@ -143,12 +145,6 @@ func dataSourceResgroup() *schema.Resource { Description: "Quota settings for this resource group.", }, - "status": { - Type: schema.TypeString, - Computed: true, - Description: "Current status of this resource group.", - }, - "def_net_type": { Type: schema.TypeString, Computed: true, @@ -162,6 +158,12 @@ func dataSourceResgroup() *schema.Resource { }, /* + "status": { + Type: schema.TypeString, + Computed: true, + Description: "Current status of this resource group.", + }, + "vins": { Type: schema.TypeList, // this is a list of ints Computed: true, diff --git a/decort/models_api.go b/decort/models_api.go index f6a2f64..09b0ea8 100644 --- a/decort/models_api.go +++ b/decort/models_api.go @@ -106,7 +106,7 @@ type ResgroupUpdateParam struct { // type QuotaRecord struct { // this is how quota is reported by /api/.../rg/get Cpu int `json:"CU_C"` // CPU count in pcs - Ram float32 `json:"CU_M"` // RAM volume in MB, it is STILL reported as FLOAT + Ram float64 `json:"CU_M"` // RAM volume in MB, it is STILL reported as FLOAT Disk int `json:"CU_D"` // Disk capacity in GB ExtIPs int `json:"CU_I"` // Ext IPs count ExtTraffic int `json:"CU_NP"` // Ext network traffic diff --git a/decort/network_subresource.go b/decort/network_subresource.go index 984e6f1..81dfc28 100644 --- a/decort/network_subresource.go +++ b/decort/network_subresource.go @@ -35,6 +35,7 @@ func networkSubresourceSchemaMake() map[string]*schema.Schema { "net_type": { Type: schema.TypeString, Required: true, + StateFunc: stateFuncToUpper, ValidateFunc: validation.StringInSlice([]string{"EXTNET", "VINS"}, false), // observe case while validating Description: "Type of the network for this connection, either EXTNET or VINS.", }, @@ -48,6 +49,7 @@ func networkSubresourceSchemaMake() map[string]*schema.Schema { "ip_address": { Type: schema.TypeString, Optional: true, + // DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {} Description: "Optional IP address to assign to this connection. This IP should belong to the selected network and free for use.", }, diff --git a/decort/provider.go b/decort/provider.go index ae34136..54be5d0 100644 --- a/decort/provider.go +++ b/decort/provider.go @@ -124,6 +124,10 @@ func stateFuncToLower(argval interface{}) string { return strings.ToLower(argval.(string)) } +func stateFuncToUpper(argval interface{}) string { + return strings.ToUpper(argval.(string)) +} + func providerConfigure(d *schema.ResourceData) (interface{}, error) { decsController, err := ControllerConfigure(d) if err != nil { diff --git a/decort/quota_subresource.go b/decort/quota_subresource.go index b8e8bde..7395f7d 100644 --- a/decort/quota_subresource.go +++ b/decort/quota_subresource.go @@ -31,7 +31,7 @@ import ( func makeQuotaRecord(arg_list []interface{}) (QuotaRecord, int) { quota := QuotaRecord{ Cpu: -1, - Ram: -1., // this is float32, but may change in the future + Ram: -1., // this is float64, but may change in the future Disk: -1, ExtTraffic: -1, ExtIPs: -1, @@ -47,8 +47,8 @@ func makeQuotaRecord(arg_list []interface{}) (QuotaRecord, int) { quota.Disk = subres_data["disk"].(int) // Disk capacity ib GB } - if subres_data["ram"].(int) > 0 { - quota.Ram = subres_data["ram"].(float32) // RAM volume in MB, as float32! + if subres_data["ram"].(float64) > 0 { + quota.Ram = subres_data["ram"].(float64) // RAM volume in MB, as float64! } if subres_data["ext_traffic"].(int) > 0 { @@ -70,7 +70,7 @@ func parseQuota(quota QuotaRecord) []interface{} { quota_map := make(map[string]interface{}) quota_map["cpu"] = quota.Cpu - quota_map["ram"] = quota.Ram // NB: this is float32, unlike the rest of values + quota_map["ram"] = quota.Ram // NB: this is float64, unlike the rest of values quota_map["disk"] = quota.Disk quota_map["ext_traffic"] = quota.ExtTraffic quota_map["ext_ips"] = quota.ExtIPs diff --git a/decort/resource_compute.go b/decort/resource_compute.go index d52fe5c..f17356a 100644 --- a/decort/resource_compute.go +++ b/decort/resource_compute.go @@ -249,7 +249,7 @@ func resourceComputeUpdate(d *schema.ResourceData, m interface{}) error { } d.Partial(false) - + // we may reuse dataSourceComputeRead here as we maintain similarity // between Compute resource and Compute data source schemas return dataSourceComputeRead(d, m) @@ -323,7 +323,7 @@ func resourceCompute() *schema.Resource { "name": { Type: schema.TypeString, Required: true, - Description: "Name of this compute. This parameter is case sensitive and must be unique in the resource group.", + Description: "Name of this compute. Compute names are case sensitive and must be unique in the resource group.", }, "rg_id": { @@ -337,6 +337,7 @@ func resourceCompute() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + StateFunc: stateFuncToUpper, ValidateFunc: validation.StringInSlice([]string{"KVM_X86", "KVM_PPC"}, false), // observe case while validating Description: "Hardware architecture of this compute instance.", }, diff --git a/decort/resource_disk.go b/decort/resource_disk.go index 27a0ade..a38631a 100644 --- a/decort/resource_disk.go +++ b/decort/resource_disk.go @@ -33,7 +33,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - // "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" ) func resourceDiskCreate(d *schema.ResourceData, m interface{}) error { @@ -234,6 +234,8 @@ func resourceDiskSchemaMake() map[string]*schema.Schema { Type: schema.TypeString, Optional: true, Default: "D", + StateFunc: stateFuncToUpper, + ValidateFunc: validation.StringInSlice([]string{"B", "D"}, false), Description: "Optional type of this disk. Defaults to D, i.e. data disk. Cannot be changed for existing disks.", }, @@ -264,14 +266,14 @@ func resourceDiskSchemaMake() map[string]*schema.Schema { }, /* - "snapshots": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource { - Schema: snapshotSubresourceSchemaMake(), - }, - Description: "List of user-created snapshots for this disk." + "snapshots": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource { + Schema: snapshotSubresourceSchemaMake(), }, + Description: "List of user-created snapshots for this disk." + }, "status": { Type: schema.TypeString, diff --git a/decort/resource_rg.go b/decort/resource_rg.go index e69b2ad..1856381 100644 --- a/decort/resource_rg.go +++ b/decort/resource_rg.go @@ -47,14 +47,19 @@ func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error { return fmt.Errorf("Cannot create new RG: missing name.") } + /* Current version of provider works with default grid id (same is true for disk resources) grid_id, arg_set := d.GetOk("grid_id") if !arg_set { return fmt.Errorf("Cannot create new RG %q in account ID %d: missing Grid ID.", rg_name.(string), validated_account_id) } + if grid_id.(int) < 1 { + grid_id = DefaultGridID + } + */ // all required parameters are set in the schema - we can continue with RG creation - log.Debugf("resourceResgroupCreate: called for RG name %q, account ID %d", + log.Debugf("resourceResgroupCreate: called for RG name %s, account ID %d", rg_name.(string), validated_account_id) // quota settings are optional @@ -68,14 +73,14 @@ func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error { } controller := m.(*ControllerCfg) - log.Debugf("resourceResgroupCreate: called by user %q for RG name %q, account ID %d, Grid ID %d", + log.Debugf("resourceResgroupCreate: called by user %q for RG name %s, account ID %d", controller.getDecortUsername(), - rg_name.(string), validated_account_id, grid_id.(int)) + rg_name.(string), validated_account_id) url_values := &url.Values{} url_values.Add("accountId", fmt.Sprintf("%d", validated_account_id)) url_values.Add("name", rg_name.(string)) - url_values.Add("gid", fmt.Sprintf("%d", grid_id.(int))) + url_values.Add("gid", fmt.Sprintf("%d", DefaultGridID)) // use default Grid ID, similar to disk resource mgmt convention url_values.Add("owner", controller.getDecortUsername()) // pass quota values as set @@ -122,8 +127,9 @@ func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error { } func resourceResgroupRead(d *schema.ResourceData, m interface{}) error { - log.Debugf("resourceResgroupRead: called for RG name %q, account name %q", - d.Get("name").(string), d.Get("account_name").(string)) + log.Debugf("resourceResgroupRead: called for RG name %s, account ID %s", + d.Get("name").(string), d.Get("account_id").(int)) + rg_facts, err := utilityResgroupCheckPresence(d, m) if rg_facts == "" { // if empty string is returned from utilityResgroupCheckPresence then there is no @@ -136,8 +142,8 @@ func resourceResgroupRead(d *schema.ResourceData, m interface{}) error { } func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error { - log.Debugf("resourceResgroupUpdate: called for RG name %q, account name %q", - d.Get("name").(string), d.Get("account").(string)) + log.Debugf("resourceResgroupUpdate: called for RG name %s, account ID %d", + d.Get("name").(string), d.Get("account_id").(int)) do_update := false @@ -219,8 +225,8 @@ func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error { func resourceResgroupDelete(d *schema.ResourceData, m interface{}) error { // NOTE: this method forcibly destroys target resource group with flag "permanently", so there is no way to // restore the destroyed resource group as well all Computes & VINSes that existed in it - log.Debugf("resourceResgroupDelete: called for RG name %q, account name %q", - d.Get("name").(string), d.Get("account_name").(string)) + log.Debugf("resourceResgroupDelete: called for RG name %s, account ID %s", + d.Get("name").(string), d.Get("account_id").(int)) rg_facts, err := utilityResgroupCheckPresence(d, m) if rg_facts == "" { @@ -266,6 +272,10 @@ func resourceResgroup() *schema.Resource { Delete: resourceResgroupDelete, Exists: resourceResgroupExists, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Timeouts: &schema.ResourceTimeout{ Create: &Timeout180s, Read: &Timeout30s, @@ -283,14 +293,8 @@ func resourceResgroup() *schema.Resource { "account_id": { Type: schema.TypeInt, - Optional: true, - Description: "Unique ID of the account, which this resource group belongs to. If account ID is specified, then account name is ignored.", - }, - - "account_name": { - Type: schema.TypeString, - Optional: true, - Description: "Name of the account, which this resource group belongs to.", + Required: true, + Description: "Unique ID of the account, which this resource group belongs to.", }, "def_net_type": { @@ -325,11 +329,16 @@ func resourceResgroup() *schema.Resource { Description: "IP address on the external netowrk to request, if def_net_type=PUBLIC", }, - "grid_id": { // change of Grid ID will require new RG + /* commented out, as in this version of provider we use default Grid ID + "grid_id": { Type: schema.TypeInt, - Required: true, + Optional: true, + Default: 0, // if 0 is passed, default Grid ID will be used + // DefaultFunc: utilityResgroupGetDefaultGridID, + ForceNew: true, // change of Grid ID will require new RG Description: "Unique ID of the grid, where this resource group is deployed.", }, + */ "quota": { Type: schema.TypeList, @@ -347,13 +356,19 @@ func resourceResgroup() *schema.Resource { Description: "User-defined text description of this resource group.", }, + "account_name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the account, which this resource group belongs to.", + }, + + /* "status": { Type: schema.TypeString, Computed: true, Description: "Current status of this resource group.", }, - /* "vins": { Type: schema.TypeList, // this is a list of ints Computed: true, diff --git a/decort/utility_account.go b/decort/utility_account.go index 94db06f..f1eaf34 100644 --- a/decort/utility_account.go +++ b/decort/utility_account.go @@ -110,6 +110,10 @@ func utilityGetAccountIdBySchema(d *schema.ResourceData, m interface{}) (int, er initiate API calls to the DECORT cloud controller and try to match relevant account by the name. + NOTE that for some resources (most notably, Resource Group) "account_name" attribute is + marked as "Computed: true", so the only way to fully identify Resource Group is to specify + "account_id", which is marked as "Required: true" + */ accId, argSet := d.GetOk("account_id") diff --git a/decort/utility_compute.go b/decort/utility_compute.go index 0437581..34fbea5 100644 --- a/decort/utility_compute.go +++ b/decort/utility_compute.go @@ -317,7 +317,7 @@ func utilityComputeCheckPresence(d *schema.ResourceData, m interface{}) (string, controller := m.(*ControllerCfg) urlValues := &url.Values{} - // make it possible to use "read" & "check presence" functions with comptue ID set so + // make it possible to use "read" & "check presence" functions with compute ID set so // that Import of Compute resource is possible idSet := false theId, err := strconv.Atoi(d.Id()) diff --git a/decort/utility_rg.go b/decort/utility_rg.go index 1f86bfe..371e678 100644 --- a/decort/utility_rg.go +++ b/decort/utility_rg.go @@ -28,6 +28,7 @@ import ( "encoding/json" "fmt" "net/url" + "strconv" log "github.com/sirupsen/logrus" @@ -43,7 +44,7 @@ func (ctrl *ControllerCfg) utilityResgroupConfigGet(rgid int) (*ResgroupGetResp, return nil, err } - log.Debugf("utilityResgroupConfigGet: ready to unmarshal string %q", rgFacts) + log.Debugf("utilityResgroupConfigGet: ready to unmarshal string %s", rgFacts) model := &ResgroupGetResp{} err = json.Unmarshal([]byte(rgFacts), model) if err != nil { @@ -90,11 +91,24 @@ func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string controller := m.(*ControllerCfg) urlValues := &url.Values{} - rgId, argSet := d.GetOk("rg_id") - if argSet { + // make it possible to use "read" & "check presence" functions with RG ID set so + // that Import of RG resource is possible + idSet := false + theId, err := strconv.Atoi(d.Id()) + if err != nil || theId <= 0 { + rgId, argSet := d.GetOk("rg_id") + if argSet { + theId = rgId.(int) + idSet = true + } + } else { + idSet = true + } + + if idSet { // go straight for the RG by its ID - log.Debugf("utilityResgroupCheckPresence: locating RG by its ID %d", rgId.(int)) - urlValues.Add("rgId", fmt.Sprintf("%d", rgId.(int))) + log.Debugf("utilityResgroupCheckPresence: locating RG by its ID %d", theId) + urlValues.Add("rgId", fmt.Sprintf("%d", theId)) rgFacts, err := controller.decortAPICall("POST", ResgroupGetAPI, urlValues) if err != nil { return "", err @@ -132,7 +146,7 @@ func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string for index, item := range model { // match by RG name & account ID if item.Name == rgName.(string) && item.AccountID == validatedAccountId { - log.Debugf("utilityResgroupCheckPresence: match RG name %q / ID %d, account ID %d at index %d", + log.Debugf("utilityResgroupCheckPresence: match RG name %s / ID %d, account ID %d at index %d", item.Name, item.ID, item.AccountID, index) // not all required information is returned by rg/list API, so we need to initiate one more @@ -149,5 +163,13 @@ func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string } } - return "", fmt.Errorf("Cannot find RG name %q owned by account ID %d", rgName, validatedAccountId) + return "", fmt.Errorf("Cannot find RG name %s owned by account ID %d", rgName, validatedAccountId) } + +func utilityResgroupGetDefaultGridID() (interface{}, error) { + if DefaultGridID > 0 { + return fmt.Sprintf("%d", DefaultGridID), nil + } + + return "", fmt.Errorf("utilityResgroupGetDefaultGridID: invalid default Grid ID %d", DefaultGridID) + }