Fix state mgmt issues for compute and add Import support for RG
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.",
|
||||
},
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.",
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user