Fix state mgmt issues for compute and add Import support for RG

rc-1.0
Sergey Shubin svs1370 4 years ago
parent 3aefa580dc
commit 7f6d11dfd5

@ -55,7 +55,7 @@ func flattenResgroup(d *schema.ResourceData, rg_facts string) error {
d.Set("name", details.Name) d.Set("name", details.Name)
d.Set("account_name", details.AccountName) d.Set("account_name", details.AccountName)
d.Set("account_id", details.AccountID) 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("description", details.Desc)
d.Set("status", details.Status) d.Set("status", details.Status)
d.Set("def_net_type", details.DefaultNetType) d.Set("def_net_type", details.DefaultNetType)
@ -111,14 +111,14 @@ func dataSourceResgroup() *schema.Resource {
"account_name": { "account_name": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Computed: true,
Description: "Name of the account, which this resource group belongs to.", Description: "Name of the account, which this resource group belongs to.",
}, },
"account_id": { "account_id": {
Type: schema.TypeInt, Type: schema.TypeInt,
Optional: true, Required: true,
Description: "Unique ID of the account, which this resource group belongs to. If account ID is specified, then account name is ignored.", Description: "Unique ID of the account, which this resource group belongs to.",
}, },
"description": { "description": {
@ -127,11 +127,13 @@ func dataSourceResgroup() *schema.Resource {
Description: "User-defined text description of this resource group.", Description: "User-defined text description of this resource group.",
}, },
/* commented out, as in this version of provider we use default Grid ID
"grid_id": { "grid_id": {
Type: schema.TypeInt, Type: schema.TypeInt,
Computed: true, Computed: true,
Description: "Unique ID of the grid, where this resource group is deployed.", Description: "Unique ID of the grid, where this resource group is deployed.",
}, },
*/
"quota": { "quota": {
Type: schema.TypeList, Type: schema.TypeList,
@ -143,12 +145,6 @@ func dataSourceResgroup() *schema.Resource {
Description: "Quota settings for this resource group.", Description: "Quota settings for this resource group.",
}, },
"status": {
Type: schema.TypeString,
Computed: true,
Description: "Current status of this resource group.",
},
"def_net_type": { "def_net_type": {
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
@ -162,6 +158,12 @@ func dataSourceResgroup() *schema.Resource {
}, },
/* /*
"status": {
Type: schema.TypeString,
Computed: true,
Description: "Current status of this resource group.",
},
"vins": { "vins": {
Type: schema.TypeList, // this is a list of ints Type: schema.TypeList, // this is a list of ints
Computed: true, Computed: true,

@ -106,7 +106,7 @@ type ResgroupUpdateParam struct {
// //
type QuotaRecord struct { // this is how quota is reported by /api/.../rg/get type QuotaRecord struct { // this is how quota is reported by /api/.../rg/get
Cpu int `json:"CU_C"` // CPU count in pcs 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 Disk int `json:"CU_D"` // Disk capacity in GB
ExtIPs int `json:"CU_I"` // Ext IPs count ExtIPs int `json:"CU_I"` // Ext IPs count
ExtTraffic int `json:"CU_NP"` // Ext network traffic ExtTraffic int `json:"CU_NP"` // Ext network traffic

@ -35,6 +35,7 @@ func networkSubresourceSchemaMake() map[string]*schema.Schema {
"net_type": { "net_type": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
StateFunc: stateFuncToUpper,
ValidateFunc: validation.StringInSlice([]string{"EXTNET", "VINS"}, false), // observe case while validating ValidateFunc: validation.StringInSlice([]string{"EXTNET", "VINS"}, false), // observe case while validating
Description: "Type of the network for this connection, either EXTNET or VINS.", Description: "Type of the network for this connection, either EXTNET or VINS.",
}, },
@ -48,6 +49,7 @@ func networkSubresourceSchemaMake() map[string]*schema.Schema {
"ip_address": { "ip_address": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, 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.", Description: "Optional IP address to assign to this connection. This IP should belong to the selected network and free for use.",
}, },

@ -124,6 +124,10 @@ func stateFuncToLower(argval interface{}) string {
return strings.ToLower(argval.(string)) return strings.ToLower(argval.(string))
} }
func stateFuncToUpper(argval interface{}) string {
return strings.ToUpper(argval.(string))
}
func providerConfigure(d *schema.ResourceData) (interface{}, error) { func providerConfigure(d *schema.ResourceData) (interface{}, error) {
decsController, err := ControllerConfigure(d) decsController, err := ControllerConfigure(d)
if err != nil { if err != nil {

@ -31,7 +31,7 @@ import (
func makeQuotaRecord(arg_list []interface{}) (QuotaRecord, int) { func makeQuotaRecord(arg_list []interface{}) (QuotaRecord, int) {
quota := QuotaRecord{ quota := QuotaRecord{
Cpu: -1, 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, Disk: -1,
ExtTraffic: -1, ExtTraffic: -1,
ExtIPs: -1, ExtIPs: -1,
@ -47,8 +47,8 @@ func makeQuotaRecord(arg_list []interface{}) (QuotaRecord, int) {
quota.Disk = subres_data["disk"].(int) // Disk capacity ib GB quota.Disk = subres_data["disk"].(int) // Disk capacity ib GB
} }
if subres_data["ram"].(int) > 0 { if subres_data["ram"].(float64) > 0 {
quota.Ram = subres_data["ram"].(float32) // RAM volume in MB, as float32! quota.Ram = subres_data["ram"].(float64) // RAM volume in MB, as float64!
} }
if subres_data["ext_traffic"].(int) > 0 { if subres_data["ext_traffic"].(int) > 0 {
@ -70,7 +70,7 @@ func parseQuota(quota QuotaRecord) []interface{} {
quota_map := make(map[string]interface{}) quota_map := make(map[string]interface{})
quota_map["cpu"] = quota.Cpu 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["disk"] = quota.Disk
quota_map["ext_traffic"] = quota.ExtTraffic quota_map["ext_traffic"] = quota.ExtTraffic
quota_map["ext_ips"] = quota.ExtIPs quota_map["ext_ips"] = quota.ExtIPs

@ -249,7 +249,7 @@ func resourceComputeUpdate(d *schema.ResourceData, m interface{}) error {
} }
d.Partial(false) d.Partial(false)
// we may reuse dataSourceComputeRead here as we maintain similarity // we may reuse dataSourceComputeRead here as we maintain similarity
// between Compute resource and Compute data source schemas // between Compute resource and Compute data source schemas
return dataSourceComputeRead(d, m) return dataSourceComputeRead(d, m)
@ -323,7 +323,7 @@ func resourceCompute() *schema.Resource {
"name": { "name": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, 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": { "rg_id": {
@ -337,6 +337,7 @@ func resourceCompute() *schema.Resource {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
ForceNew: true, ForceNew: true,
StateFunc: stateFuncToUpper,
ValidateFunc: validation.StringInSlice([]string{"KVM_X86", "KVM_PPC"}, false), // observe case while validating ValidateFunc: validation.StringInSlice([]string{"KVM_X86", "KVM_PPC"}, false), // observe case while validating
Description: "Hardware architecture of this compute instance.", Description: "Hardware architecture of this compute instance.",
}, },

@ -33,7 +33,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" "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 { func resourceDiskCreate(d *schema.ResourceData, m interface{}) error {
@ -234,6 +234,8 @@ func resourceDiskSchemaMake() map[string]*schema.Schema {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
Default: "D", 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.", 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": { "snapshots": {
Type: schema.TypeList, Type: schema.TypeList,
Computed: true, Computed: true,
Elem: &schema.Resource { Elem: &schema.Resource {
Schema: snapshotSubresourceSchemaMake(), Schema: snapshotSubresourceSchemaMake(),
},
Description: "List of user-created snapshots for this disk."
}, },
Description: "List of user-created snapshots for this disk."
},
"status": { "status": {
Type: schema.TypeString, Type: schema.TypeString,

@ -47,14 +47,19 @@ func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
return fmt.Errorf("Cannot create new RG: missing name.") 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") grid_id, arg_set := d.GetOk("grid_id")
if !arg_set { if !arg_set {
return fmt.Errorf("Cannot create new RG %q in account ID %d: missing Grid ID.", return fmt.Errorf("Cannot create new RG %q in account ID %d: missing Grid ID.",
rg_name.(string), validated_account_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 // 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) rg_name.(string), validated_account_id)
// quota settings are optional // quota settings are optional
@ -68,14 +73,14 @@ func resourceResgroupCreate(d *schema.ResourceData, m interface{}) error {
} }
controller := m.(*ControllerCfg) 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(), controller.getDecortUsername(),
rg_name.(string), validated_account_id, grid_id.(int)) rg_name.(string), validated_account_id)
url_values := &url.Values{} url_values := &url.Values{}
url_values.Add("accountId", fmt.Sprintf("%d", validated_account_id)) url_values.Add("accountId", fmt.Sprintf("%d", validated_account_id))
url_values.Add("name", rg_name.(string)) 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()) url_values.Add("owner", controller.getDecortUsername())
// pass quota values as set // 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 { func resourceResgroupRead(d *schema.ResourceData, m interface{}) error {
log.Debugf("resourceResgroupRead: called for RG name %q, account name %q", log.Debugf("resourceResgroupRead: called for RG name %s, account ID %s",
d.Get("name").(string), d.Get("account_name").(string)) d.Get("name").(string), d.Get("account_id").(int))
rg_facts, err := utilityResgroupCheckPresence(d, m) rg_facts, err := utilityResgroupCheckPresence(d, m)
if rg_facts == "" { if rg_facts == "" {
// if empty string is returned from utilityResgroupCheckPresence then there is no // 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 { func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error {
log.Debugf("resourceResgroupUpdate: called for RG name %q, account name %q", log.Debugf("resourceResgroupUpdate: called for RG name %s, account ID %d",
d.Get("name").(string), d.Get("account").(string)) d.Get("name").(string), d.Get("account_id").(int))
do_update := false do_update := false
@ -219,8 +225,8 @@ func resourceResgroupUpdate(d *schema.ResourceData, m interface{}) error {
func resourceResgroupDelete(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 // 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 // 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", log.Debugf("resourceResgroupDelete: called for RG name %s, account ID %s",
d.Get("name").(string), d.Get("account_name").(string)) d.Get("name").(string), d.Get("account_id").(int))
rg_facts, err := utilityResgroupCheckPresence(d, m) rg_facts, err := utilityResgroupCheckPresence(d, m)
if rg_facts == "" { if rg_facts == "" {
@ -266,6 +272,10 @@ func resourceResgroup() *schema.Resource {
Delete: resourceResgroupDelete, Delete: resourceResgroupDelete,
Exists: resourceResgroupExists, Exists: resourceResgroupExists,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{ Timeouts: &schema.ResourceTimeout{
Create: &Timeout180s, Create: &Timeout180s,
Read: &Timeout30s, Read: &Timeout30s,
@ -283,14 +293,8 @@ func resourceResgroup() *schema.Resource {
"account_id": { "account_id": {
Type: schema.TypeInt, Type: schema.TypeInt,
Optional: true, Required: true,
Description: "Unique ID of the account, which this resource group belongs to. If account ID is specified, then account name is ignored.", Description: "Unique ID of the account, which this resource group belongs to.",
},
"account_name": {
Type: schema.TypeString,
Optional: true,
Description: "Name of the account, which this resource group belongs to.",
}, },
"def_net_type": { "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", 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, 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.", Description: "Unique ID of the grid, where this resource group is deployed.",
}, },
*/
"quota": { "quota": {
Type: schema.TypeList, Type: schema.TypeList,
@ -347,13 +356,19 @@ func resourceResgroup() *schema.Resource {
Description: "User-defined text description of this resource group.", 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": { "status": {
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
Description: "Current status of this resource group.", Description: "Current status of this resource group.",
}, },
/*
"vins": { "vins": {
Type: schema.TypeList, // this is a list of ints Type: schema.TypeList, // this is a list of ints
Computed: true, Computed: true,

@ -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 initiate API calls to the DECORT cloud controller and try to match relevant account
by the name. 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") accId, argSet := d.GetOk("account_id")

@ -317,7 +317,7 @@ func utilityComputeCheckPresence(d *schema.ResourceData, m interface{}) (string,
controller := m.(*ControllerCfg) controller := m.(*ControllerCfg)
urlValues := &url.Values{} 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 // that Import of Compute resource is possible
idSet := false idSet := false
theId, err := strconv.Atoi(d.Id()) theId, err := strconv.Atoi(d.Id())

@ -28,6 +28,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url" "net/url"
"strconv"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -43,7 +44,7 @@ func (ctrl *ControllerCfg) utilityResgroupConfigGet(rgid int) (*ResgroupGetResp,
return nil, err return nil, err
} }
log.Debugf("utilityResgroupConfigGet: ready to unmarshal string %q", rgFacts) log.Debugf("utilityResgroupConfigGet: ready to unmarshal string %s", rgFacts)
model := &ResgroupGetResp{} model := &ResgroupGetResp{}
err = json.Unmarshal([]byte(rgFacts), model) err = json.Unmarshal([]byte(rgFacts), model)
if err != nil { if err != nil {
@ -90,11 +91,24 @@ func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string
controller := m.(*ControllerCfg) controller := m.(*ControllerCfg)
urlValues := &url.Values{} urlValues := &url.Values{}
rgId, argSet := d.GetOk("rg_id") // make it possible to use "read" & "check presence" functions with RG ID set so
if argSet { // 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 // go straight for the RG by its ID
log.Debugf("utilityResgroupCheckPresence: locating RG by its ID %d", rgId.(int)) log.Debugf("utilityResgroupCheckPresence: locating RG by its ID %d", theId)
urlValues.Add("rgId", fmt.Sprintf("%d", rgId.(int))) urlValues.Add("rgId", fmt.Sprintf("%d", theId))
rgFacts, err := controller.decortAPICall("POST", ResgroupGetAPI, urlValues) rgFacts, err := controller.decortAPICall("POST", ResgroupGetAPI, urlValues)
if err != nil { if err != nil {
return "", err return "", err
@ -132,7 +146,7 @@ func utilityResgroupCheckPresence(d *schema.ResourceData, m interface{}) (string
for index, item := range model { for index, item := range model {
// match by RG name & account ID // match by RG name & account ID
if item.Name == rgName.(string) && item.AccountID == validatedAccountId { 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) item.Name, item.ID, item.AccountID, index)
// not all required information is returned by rg/list API, so we need to initiate one more // 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)
}

Loading…
Cancel
Save