From 4a326e9ceb45f1fd7dfd9e05ccd13b7981f64c35 Mon Sep 17 00:00:00 2001 From: stSolo Date: Fri, 27 May 2022 12:26:11 +0300 Subject: [PATCH] Add a resource account --- decort/data_source_account.go | 84 +++- decort/data_source_account_list.go | 58 ++- decort/data_source_rg_list.go | 4 +- decort/models_api.go | 86 ++-- decort/provider.go | 1 + decort/resource_account.go | 729 +++++++++++++++++++++++++++++ decort/utility_account.go | 6 +- decort/utility_account_list.go | 28 ++ samples/resource_account/main.tf | 59 +++ 9 files changed, 1003 insertions(+), 52 deletions(-) create mode 100644 decort/resource_account.go create mode 100644 samples/resource_account/main.tf diff --git a/decort/data_source_account.go b/decort/data_source_account.go index 2db3008..39ef95c 100644 --- a/decort/data_source_account.go +++ b/decort/data_source_account.go @@ -41,7 +41,7 @@ func dataSourceAccountRead(d *schema.ResourceData, m interface{}) error { d.Set("resources", flattenAccResources(acc.Resources)) d.Set("ckey", acc.CKey) d.Set("meta", flattenMeta(acc.Meta)) - d.Set("acl", flattenRgAcl(acc.Acl)) + d.Set("acl", flattenAccAcl(acc.Acl)) d.Set("company", acc.Company) d.Set("companyurl", acc.CompanyUrl) d.Set("created_by", acc.CreatedBy) @@ -60,9 +60,49 @@ func dataSourceAccountRead(d *schema.ResourceData, m interface{}) error { d.Set("updated_time", acc.UpdatedTime) d.Set("version", acc.Version) d.Set("vins", acc.Vins) + d.Set("vinses", acc.Vinses) + d.Set("computes", flattenAccComputes(acc.Computes)) + d.Set("machines", flattenAccMachines(acc.Machines)) return nil } +func flattenAccComputes(acs Computes) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + temp := map[string]interface{}{ + "started": acs.Started, + "stopped": acs.Stopped, + } + res = append(res, temp) + return res +} + +func flattenAccMachines(ams Machines) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + temp := map[string]interface{}{ + "running": ams.Running, + "halted": ams.Halted, + } + res = append(res, temp) + return res +} + +func flattenAccAcl(acls []AccountAclRecord) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + for _, acls := range acls { + temp := map[string]interface{}{ + "can_be_deleted": acls.CanBeDeleted, + "explicit": acls.IsExplicit, + "guid": acls.Guid, + "right": acls.Rights, + "status": acls.Status, + "type": acls.Type, + "user_group_id": acls.UgroupID, + } + res = append(res, temp) + } + return res +} + func flattenAccResources(r Resources) []map[string]interface{} { res := make([]map[string]interface{}, 0) temp := map[string]interface{}{ @@ -188,6 +228,10 @@ func dataSourceAccountSchemaMake() map[string]*schema.Schema { Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "can_be_deleted": { + Type: schema.TypeBool, + Computed: true, + }, "explicit": { Type: schema.TypeBool, Computed: true, @@ -315,6 +359,44 @@ func dataSourceAccountSchemaMake() map[string]*schema.Schema { Type: schema.TypeInt, }, }, + "computes": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "started": { + Type: schema.TypeInt, + Computed: true, + }, + "stopped": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "machines": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "halted": { + Type: schema.TypeInt, + Computed: true, + }, + "running": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "vinses": { + Type: schema.TypeInt, + Computed: true, + }, } return res } diff --git a/decort/data_source_account_list.go b/decort/data_source_account_list.go index 866f232..b587bda 100644 --- a/decort/data_source_account_list.go +++ b/decort/data_source_account_list.go @@ -29,6 +29,24 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) +func flattenAccountList(al AccountCloudApiList) []map[string]interface{} { + res := make([]map[string]interface{}, 0) + for _, acc := range al { + temp := map[string]interface{}{ + "acl": flattenRgAcl(acc.Acl), + "created_time": acc.CreatedTime, + "deleted_time": acc.DeletedTime, + "account_id": acc.ID, + "account_name": acc.Name, + "status": acc.Status, + "updated_time": acc.UpdatedTime, + } + res = append(res, temp) + } + return res +} + +/*uncomment for cloudbroker func flattenAccountList(al AccountList) []map[string]interface{} { res := make([]map[string]interface{}, 0) for _, acc := range al { @@ -36,31 +54,42 @@ func flattenAccountList(al AccountList) []map[string]interface{} { "dc_location": acc.DCLocation, "ckey": acc.CKey, "meta": flattenMeta(acc.Meta), - "acl": flattenRgAcl(acc.Acl), + + "acl": flattenRgAcl(acc.Acl), + "company": acc.Company, "companyurl": acc.CompanyUrl, "created_by": acc.CreatedBy, - "created_time": acc.CreatedTime, + + "created_time": acc.CreatedTime, + "deactivation_time": acc.DeactiovationTime, "deleted_by": acc.DeletedBy, - "deleted_time": acc.DeletedTime, + + "deleted_time": acc.DeletedTime, + "displayname": acc.DisplayName, "guid": acc.GUID, - "account_id": acc.ID, - "account_name": acc.Name, + + "account_id": acc.ID, + "account_name": acc.Name, + "resource_limits": flattenRgResourceLimits(acc.ResourceLimits), "send_access_emails": acc.SendAccessEmails, "service_account": acc.ServiceAccount, - "status": acc.Status, - "updated_time": acc.UpdatedTime, + + "status": acc.Status, + "updated_time": acc.UpdatedTime, + "version": acc.Version, "vins": acc.Vins, + } res = append(res, temp) } return res - } +*/ func dataSourceAccountListRead(d *schema.ResourceData, m interface{}) error { accountList, err := utilityAccountListCheckPresence(d, m) @@ -92,6 +121,7 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema { Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + /*uncomment for cloudbroker "dc_location": { Type: schema.TypeString, Computed: true, @@ -106,7 +136,7 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema { Elem: &schema.Schema{ Type: schema.TypeString, }, - }, + },*/ "acl": { Type: schema.TypeList, Computed: true, @@ -139,6 +169,7 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema { }, }, }, + /*uncomment for cloudbroker "company": { Type: schema.TypeString, Computed: true, @@ -151,10 +182,12 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema { Type: schema.TypeString, Computed: true, }, + */ "created_time": { Type: schema.TypeInt, Computed: true, }, + /*uncomment for cloudbroker "deactivation_time": { Type: schema.TypeFloat, Computed: true, @@ -163,10 +196,12 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema { Type: schema.TypeString, Computed: true, }, + */ "deleted_time": { Type: schema.TypeInt, Computed: true, }, + /*uncomment for cloudbroker "displayname": { Type: schema.TypeString, Computed: true, @@ -175,6 +210,7 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema { Type: schema.TypeInt, Computed: true, }, + */ "account_id": { Type: schema.TypeInt, Computed: true, @@ -183,6 +219,7 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema { Type: schema.TypeString, Computed: true, }, + /*uncomment for cloudbroker "resource_limits": { Type: schema.TypeList, Computed: true, @@ -224,6 +261,7 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema { Type: schema.TypeBool, Computed: true, }, + */ "status": { Type: schema.TypeString, Computed: true, @@ -232,6 +270,7 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema { Type: schema.TypeInt, Computed: true, }, + /*uncomment for cloudbroker "version": { Type: schema.TypeInt, Computed: true, @@ -243,6 +282,7 @@ func dataSourceAccountListSchemaMake() map[string]*schema.Schema { Type: schema.TypeInt, }, }, + */ }, }, }, diff --git a/decort/data_source_rg_list.go b/decort/data_source_rg_list.go index 9220693..73afce5 100644 --- a/decort/data_source_rg_list.go +++ b/decort/data_source_rg_list.go @@ -1,6 +1,6 @@ /* -Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved. -Author: Sergey Shubin, , +Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved. +Author: Stanislav Solovev, Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/decort/models_api.go b/decort/models_api.go index 3c07f8b..80b5ffd 100644 --- a/decort/models_api.go +++ b/decort/models_api.go @@ -51,12 +51,13 @@ type UserAclRecord struct { } type AccountAclRecord struct { - IsExplicit bool `json:"explicit"` - Guid string `json:"guid"` - Rights string `json:"right"` - Status string `json:"status"` - Type string `json:"type"` - UgroupID string `json:"userGroupId"` + IsExplicit bool `json:"explicit"` + Guid string `json:"guid"` + Rights string `json:"right"` + Status string `json:"status"` + Type string `json:"type"` + UgroupID string `json:"userGroupId"` + CanBeDeleted bool `json:"canBeDeleted"` } type ResourceLimits struct { @@ -992,46 +993,28 @@ type SepPool map[string]interface{} ///// ACCOUNTS //// /////////////////////// -const accountAddUserAPI = "/cloudapi/account/addUser" - -//const accountAuditsAPI = "/restmachine/cloudapi/account/audits" -const accountAuditsAPI = "/restmachine/cloudbroker/account/audits" -const accountCreateAPI = "/cloudapi/account/create" -const accountDeleteAPI = "/cloudapi/account/delete" +const accountAddUserAPI = "/restmachine/cloudapi/account/addUser" +const accountAuditsAPI = "/restmachine/cloudapi/account/audits" +const accountCreateAPI = "/restmachine/cloudapi/account/create" +const accountDeleteAPI = "/restmachine/cloudapi/account/delete" const accountDeleteAccountsAPI = "/cloudapi/account/deleteAccounts" -const accountDeleteUserAPI = "/cloudapi/account/deleteUser" -const accountDisableAPI = "/cloudapi/account/disable" -const accountEnableAPI = "/cloudapi/account/enable" - -//const accountGetAPI = "/restmachine/cloudapi/account/get" -const accountGetAPI = "/restmachine/cloudbroker/account/get" -const accountGetConsumedAccountUnitsAPI = "/cloudapi/account/getConsumedAccountUnits" -const accountGetConsumedCloudUnitsByTypeAPI = "/cloudapi/account/getConsumedCloudUnitsByType" -const accountGetConsumptionGetAPI = "/cloudapi/account/getConsumption" -const accountGetConsumptionPostAPI = "/cloudapi/account/getConsumption" -const accountGetReservedAccountUnitsAPI = "/cloudapi/account/getReservedAccountUnits" -const accountGetStatsAPI = "/cloudapi/account/getStats" - -//const accountListAPI = "/restmachine/cloudapi/account/list" -const accountListAPI = "/restmachine/cloudbroker/account/list" - -//const accountListComputesAPI = "/restmachine/cloudapi/account/listComputes" -const accountListComputesAPI = "/restmachine/cloudbroker/account/listComputes" +const accountDeleteUserAPI = "/restmachine/cloudapi/account/deleteUser" +const accountDisableAPI = "/restmachine/cloudapi/account/disable" +const accountEnableAPI = "/restmachine/cloudapi/account/enable" +const accountGetAPI = "/restmachine/cloudapi/account/get" +const accountListAPI = "/restmachine/cloudapi/account/list" +const accountListComputesAPI = "/restmachine/cloudapi/account/listComputes" const accountListCSAPI = "/cloudapi/account/listCS" const accountListDeletedAPI = "/cloudapi/account/listDeleted" - -//const accountListDisksAPI = "/restmachine/cloudapi/account/listDisks" -const accountListDisksAPI = "/restmachine/cloudbroker/account/listDisks" +const accountListDisksAPI = "/restmachine/cloudapi/account/listDisks" const accountListFlipGroupsAPI = "/cloudapi/account/listFlipGroups" const accountListRGAPI = "/cloudapi/account/listRG" const accountListTemplatesAPI = "/cloudapi/account/listTemplates" - -//const accountListVinsAPI = "/restmachine/cloudapi/account/listVins" -const accountListVinsAPI = "/restmachine/cloudbroker/account/listVins" +const accountListVinsAPI = "/restmachine/cloudapi/account/listVins" const accountListVMsAPI = "/cloudapi/account/listVMs" -const accountRestoreAPI = "/cloudapi/account/restore" -const accountUpdateAPI = "/cloudapi/account/update" -const accountUpdateUserAPI = "/cloudapi/account/updateUser" +const accountRestoreAPI = "/restmachine/cloudapi/account/restore" +const accountUpdateAPI = "/restmachine/cloudapi/account/update" +const accountUpdateUserAPI = "/restmachine/cloudapi/account/updateUser" ////Structs @@ -1062,6 +1045,18 @@ type Account struct { type AccountList []Account +type AccountCloudApi struct { + Acl []AccountAclRecord `json:"acl"` + CreatedTime int `json:"createdTime"` + DeletedTime int `json:"deletedTime"` + ID int `json:"id"` + Name string `json:"name"` + Status string `json:"status"` + UpdatedTime int `json:"updatedTime"` +} + +type AccountCloudApiList []AccountCloudApi + type Resource struct { CPU int `json:"cpu"` Disksize int `json:"disksize"` @@ -1076,9 +1071,22 @@ type Resources struct { Reserved Resource `json:"Reserved"` } +type Computes struct { + Started int `json:"started"` + Stopped int `json:"stopped"` +} + +type Machines struct { + Running int `json:"running"` + Halted int `json:"halted"` +} + type AccountWithResources struct { Account Resources Resources `json:"Resources"` + Computes Computes `json:"computes"` + Machines Machines `json:"machines"` + Vinses int `json:"vinses"` } type AccountCompute struct { diff --git a/decort/provider.go b/decort/provider.go index b2f9e45..7145229 100644 --- a/decort/provider.go +++ b/decort/provider.go @@ -114,6 +114,7 @@ func Provider() *schema.Provider { "decort_pcidevice": resourcePcidevice(), "decort_sep": resourceSep(), "decort_sep_config": resourceSepConfig(), + "decort_account": resourceAccount(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/decort/resource_account.go b/decort/resource_account.go new file mode 100644 index 0000000..05cd238 --- /dev/null +++ b/decort/resource_account.go @@ -0,0 +1,729 @@ +/* +Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved. +Author: Stanislav Solovev, + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration +Technology platfom. + +Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. +*/ + +package decort + +import ( + "errors" + "net/url" + "strconv" + "strings" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + log "github.com/sirupsen/logrus" +) + +func resourceAccountCreate(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceAccountCreate") + + if accountId, ok := d.GetOk("account_id"); ok { + if exists, err := resourceAccountExists(d, m); exists { + if err != nil { + return err + } + d.SetId(strconv.Itoa(accountId.(int))) + err = resourceAccountRead(d, m) + if err != nil { + return err + } + + return nil + } + return errors.New("provided sep id does not exist") + } + + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + + urlValues.Add("name", d.Get("account_name").(string)) + urlValues.Add("username", d.Get("username").(string)) + + if emailaddress, ok := d.GetOk("emailaddress"); ok { + urlValues.Add("emailaddress", emailaddress.(string)) + } + if sendAccessEmails, ok := d.GetOk("send_access_emails"); ok { + urlValues.Add("sendAccessEmails", strconv.FormatBool(sendAccessEmails.(bool))) + } + if resLimits, ok := d.GetOk("resource_limits"); ok { + resLimit := resLimits.([]interface{})[0] + resLimitConv := resLimit.(map[string]interface{}) + + if resLimitConv["cu_m"] != nil { + urlValues.Add("maxMemoryCapacity", strconv.Itoa(int(resLimitConv["cu_m"].(float64)))) + } + if resLimitConv["cu_d"] != nil { + urlValues.Add("maxVDiskCapacity", strconv.Itoa(int(resLimitConv["cu_d"].(float64)))) + } + if resLimitConv["cu_c"] != nil { + urlValues.Add("maxCPUCapacity", strconv.Itoa(int(resLimitConv["cu_c"].(float64)))) + } + if resLimitConv["cu_i"] != nil { + urlValues.Add("maxNumPublicIP", strconv.Itoa(int(resLimitConv["cu_i"].(float64)))) + } + if resLimitConv["cu_np"] != nil { + urlValues.Add("maxNetworkPeerTransfer", strconv.Itoa(int(resLimitConv["cu_np"].(float64)))) + } + if resLimitConv["gpu_units"] != nil { + urlValues.Add("gpu_units", strconv.Itoa(int(resLimitConv["gpu_units"].(float64)))) + } + } + + accountId, err := controller.decortAPICall("POST", accountCreateAPI, urlValues) + if err != nil { + return err + } + + id := uuid.New() + d.SetId(accountId) + d.Set("account_id", accountId) + + err = resourceAccountRead(d, m) + if err != nil { + return err + } + + d.SetId(id.String()) + + return nil +} + +func resourceAccountRead(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceSepRead") + + acc, err := utilityAccountCheckPresence(d, m) + if acc == nil { + d.SetId("") + return err + } + + d.Set("dc_location", acc.DCLocation) + d.Set("resources", flattenAccResources(acc.Resources)) + d.Set("ckey", acc.CKey) + d.Set("meta", flattenMeta(acc.Meta)) + d.Set("acl", flattenAccAcl(acc.Acl)) + d.Set("company", acc.Company) + d.Set("companyurl", acc.CompanyUrl) + d.Set("created_by", acc.CreatedBy) + d.Set("created_time", acc.CreatedTime) + d.Set("deactivation_time", acc.DeactiovationTime) + d.Set("deleted_by", acc.DeletedBy) + d.Set("deleted_time", acc.DeletedTime) + d.Set("displayname", acc.DisplayName) + d.Set("guid", acc.GUID) + d.Set("account_id", acc.ID) + d.Set("account_name", acc.Name) + d.Set("resource_limits", flattenRgResourceLimits(acc.ResourceLimits)) + d.Set("send_access_emails", acc.SendAccessEmails) + d.Set("service_account", acc.ServiceAccount) + d.Set("status", acc.Status) + d.Set("updated_time", acc.UpdatedTime) + d.Set("version", acc.Version) + d.Set("vins", acc.Vins) + d.Set("vinses", acc.Vinses) + d.Set("computes", flattenAccComputes(acc.Computes)) + d.Set("machines", flattenAccMachines(acc.Machines)) + + return nil +} + +func resourceAccountDelete(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceAccountDelete") + + account, err := utilityAccountCheckPresence(d, m) + if account == nil { + if err != nil { + return err + } + return nil + } + + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) + urlValues.Add("permanently", strconv.FormatBool(d.Get("permanently").(bool))) + + _, err = controller.decortAPICall("POST", accountDeleteAPI, urlValues) + if err != nil { + return err + } + d.SetId("") + + return nil +} + +func resourceAccountExists(d *schema.ResourceData, m interface{}) (bool, error) { + log.Debugf("resourceAccountExists") + + account, err := utilityAccountCheckPresence(d, m) + if account == nil { + if err != nil { + return false, err + } + return false, nil + } + + return true, nil +} + +func resourceAccountEdit(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceAccountEdit") + c := m.(*ControllerCfg) + + urlValues := &url.Values{} + if d.HasChange("enable") { + api := accountDisableAPI + enable := d.Get("enable").(bool) + if enable { + api = accountEnableAPI + } + urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) + + _, err := c.decortAPICall("POST", api, urlValues) + if err != nil { + return err + } + + urlValues = &url.Values{} + } + + if d.HasChange("account_name") { + urlValues.Add("name", d.Get("account_name").(string)) + urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) + _, err := c.decortAPICall("POST", accountUpdateAPI, urlValues) + if err != nil { + return err + } + + urlValues = &url.Values{} + } + if d.HasChange("resource_limits") { + resLimit := d.Get("resource_limits").([]interface{})[0] + resLimitConv := resLimit.(map[string]interface{}) + + if resLimitConv["cu_m"] != nil { + urlValues.Add("maxMemoryCapacity", strconv.Itoa(int(resLimitConv["cu_m"].(float64)))) + } + if resLimitConv["cu_d"] != nil { + urlValues.Add("maxVDiskCapacity", strconv.Itoa(int(resLimitConv["cu_d"].(float64)))) + } + if resLimitConv["cu_c"] != nil { + urlValues.Add("maxCPUCapacity", strconv.Itoa(int(resLimitConv["cu_c"].(float64)))) + } + if resLimitConv["cu_i"] != nil { + urlValues.Add("maxNumPublicIP", strconv.Itoa(int(resLimitConv["cu_i"].(float64)))) + } + if resLimitConv["cu_np"] != nil { + urlValues.Add("maxNetworkPeerTransfer", strconv.Itoa(int(resLimitConv["cu_np"].(float64)))) + } + if resLimitConv["gpu_units"] != nil { + urlValues.Add("gpu_units", strconv.Itoa(int(resLimitConv["gpu_units"].(float64)))) + } + + urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) + _, err := c.decortAPICall("POST", accountUpdateAPI, urlValues) + if err != nil { + return err + } + + urlValues = &url.Values{} + } + + if d.HasChange("send_access_emails") { + urlValues.Add("sendAccessEmails", strconv.FormatBool(d.Get("send_access_emails").(bool))) + urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) + _, err := c.decortAPICall("POST", accountUpdateAPI, urlValues) + if err != nil { + return err + } + + urlValues = &url.Values{} + } + + if d.HasChange("restore") { + restore := d.Get("restore").(bool) + if restore { + urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) + _, err := c.decortAPICall("POST", accountRestoreAPI, urlValues) + if err != nil { + return err + } + + urlValues = &url.Values{} + } + } + + if d.HasChange("users") { + deletedUsers := make([]interface{}, 0) + addedUsers := make([]interface{}, 0) + updatedUsers := make([]interface{}, 0) + + old, new := d.GetChange("users") + oldConv := old.([]interface{}) + newConv := new.([]interface{}) + for _, el := range oldConv { + if !isContainsUser(newConv, el) { + deletedUsers = append(deletedUsers, el) + } + } + for _, el := range newConv { + if !isContainsUser(oldConv, el) { + addedUsers = append(addedUsers, el) + } else { + if isChangedUser(oldConv, el) { + updatedUsers = append(updatedUsers, el) + } + } + } + + if len(deletedUsers) > 0 { + for _, user := range deletedUsers { + userConv := user.(map[string]interface{}) + urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) + urlValues.Add("userId", userConv["user_id"].(string)) + urlValues.Add("recursivedelete", strconv.FormatBool(userConv["recursive_delete"].(bool))) + _, err := c.decortAPICall("POST", accountDeleteUserAPI, urlValues) + if err != nil { + return err + } + + urlValues = &url.Values{} + } + } + + if len(addedUsers) > 0 { + for _, user := range addedUsers { + userConv := user.(map[string]interface{}) + urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) + urlValues.Add("userId", userConv["user_id"].(string)) + urlValues.Add("accesstype", strings.ToUpper(userConv["access_type"].(string))) + _, err := c.decortAPICall("POST", accountAddUserAPI, urlValues) + if err != nil { + return err + } + + urlValues = &url.Values{} + } + } + + if len(updatedUsers) > 0 { + for _, user := range updatedUsers { + userConv := user.(map[string]interface{}) + urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) + urlValues.Add("userId", userConv["user_id"].(string)) + urlValues.Add("accesstype", strings.ToUpper(userConv["access_type"].(string))) + _, err := c.decortAPICall("POST", accountUpdateUserAPI, urlValues) + if err != nil { + return err + } + + urlValues = &url.Values{} + } + } + + } + + return nil +} + +func isContainsUser(els []interface{}, el interface{}) bool { + for _, elOld := range els { + elOldConv := elOld.(map[string]interface{}) + elConv := el.(map[string]interface{}) + if elOldConv["user_id"].(string) == elConv["user_id"].(string) { + return true + } + } + return false +} + +func isChangedUser(els []interface{}, el interface{}) bool { + for _, elOld := range els { + elOldConv := elOld.(map[string]interface{}) + elConv := el.(map[string]interface{}) + if elOldConv["user_id"].(string) == elConv["user_id"].(string) && + (strings.ToUpper(elOldConv["access_type"].(string)) != strings.ToUpper(elConv["access_type"].(string)) || + elOldConv["recursive_delete"].(bool) != elConv["recursive_delete"].(bool)) { + return true + } + } + return false +} + +func resourceAccountSchemaMake() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "account_name": { + Type: schema.TypeString, + Required: true, + Description: "account name", + }, + "username": { + Type: schema.TypeString, + Required: true, + Description: "username of owner the account", + }, + "emailaddress": { + Type: schema.TypeString, + Optional: true, + Description: "email", + }, + "send_access_emails": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "if true send emails when a user is granted access to resources", + }, + "users": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_id": { + Type: schema.TypeString, + Required: true, + }, + "access_type": { + Type: schema.TypeString, + Required: true, + }, + "recursive_delete": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + }, + "restore": { + Type: schema.TypeBool, + Optional: true, + Description: "restore a deleted account", + }, + "permanently": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "whether to completely delete the account", + }, + "enable": { + Type: schema.TypeBool, + Optional: true, + Description: "enable/disable account", + }, + "resource_limits": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cu_c": { + Type: schema.TypeFloat, + Optional: true, + Computed: true, + }, + "cu_d": { + Type: schema.TypeFloat, + Optional: true, + Computed: true, + }, + "cu_i": { + Type: schema.TypeFloat, + Optional: true, + Computed: true, + }, + "cu_m": { + Type: schema.TypeFloat, + Optional: true, + Computed: true, + }, + "cu_np": { + Type: schema.TypeFloat, + Optional: true, + Computed: true, + }, + "gpu_units": { + Type: schema.TypeFloat, + Optional: true, + Computed: true, + }, + }, + }, + }, + "account_id": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + "dc_location": { + Type: schema.TypeString, + Computed: true, + }, + "resources": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "current": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cpu": { + Type: schema.TypeInt, + Computed: true, + }, + "disksize": { + Type: schema.TypeInt, + Computed: true, + }, + "extips": { + Type: schema.TypeInt, + Computed: true, + }, + "exttraffic": { + Type: schema.TypeInt, + Computed: true, + }, + "gpu": { + Type: schema.TypeInt, + Computed: true, + }, + "ram": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "reserved": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cpu": { + Type: schema.TypeInt, + Computed: true, + }, + "disksize": { + Type: schema.TypeInt, + Computed: true, + }, + "extips": { + Type: schema.TypeInt, + Computed: true, + }, + "exttraffic": { + Type: schema.TypeInt, + Computed: true, + }, + "gpu": { + Type: schema.TypeInt, + Computed: true, + }, + "ram": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "ckey": { + Type: schema.TypeString, + Computed: true, + }, + "meta": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "acl": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "can_be_deleted": { + Type: schema.TypeBool, + Computed: true, + }, + "explicit": { + Type: schema.TypeBool, + Computed: true, + }, + "guid": { + Type: schema.TypeString, + Computed: true, + }, + "right": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + "user_group_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "company": { + Type: schema.TypeString, + Computed: true, + }, + "companyurl": { + Type: schema.TypeString, + Computed: true, + }, + "created_by": { + Type: schema.TypeString, + Computed: true, + }, + "created_time": { + Type: schema.TypeInt, + Computed: true, + }, + "deactivation_time": { + Type: schema.TypeFloat, + Computed: true, + }, + "deleted_by": { + Type: schema.TypeString, + Computed: true, + }, + "deleted_time": { + Type: schema.TypeInt, + Computed: true, + }, + "displayname": { + Type: schema.TypeString, + Computed: true, + }, + "guid": { + Type: schema.TypeInt, + Computed: true, + }, + "service_account": { + Type: schema.TypeBool, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "updated_time": { + Type: schema.TypeInt, + Computed: true, + }, + "version": { + Type: schema.TypeInt, + Computed: true, + }, + "vins": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "computes": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "started": { + Type: schema.TypeInt, + Computed: true, + }, + "stopped": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "machines": { + Type: schema.TypeList, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "halted": { + Type: schema.TypeInt, + Computed: true, + }, + "running": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "vinses": { + Type: schema.TypeInt, + Computed: true, + }, + } +} + +func resourceAccount() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + + Create: resourceAccountCreate, + Read: resourceAccountRead, + Update: resourceAccountEdit, + Delete: resourceAccountDelete, + Exists: resourceAccountExists, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: &Timeout60s, + Read: &Timeout30s, + Update: &Timeout60s, + Delete: &Timeout60s, + Default: &Timeout60s, + }, + + Schema: resourceAccountSchemaMake(), + } +} diff --git a/decort/utility_account.go b/decort/utility_account.go index f2b5e2e..136a939 100644 --- a/decort/utility_account.go +++ b/decort/utility_account.go @@ -39,7 +39,11 @@ func utilityAccountCheckPresence(d *schema.ResourceData, m interface{}) (*Accoun controller := m.(*ControllerCfg) urlValues := &url.Values{} - urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) + if (strconv.Itoa(d.Get("account_id").(int))) != "0" { + urlValues.Add("accountId", strconv.Itoa(d.Get("account_id").(int))) + } else { + urlValues.Add("accountId", d.Id()) + } log.Debugf("utilityAccountCheckPresence: load account") accountRaw, err := controller.decortAPICall("POST", accountGetAPI, urlValues) diff --git a/decort/utility_account_list.go b/decort/utility_account_list.go index 828ecf9..0dd3514 100644 --- a/decort/utility_account_list.go +++ b/decort/utility_account_list.go @@ -34,6 +34,33 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) +func utilityAccountListCheckPresence(d *schema.ResourceData, m interface{}) (AccountCloudApiList, error) { + accountList := AccountCloudApiList{} + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + + if page, ok := d.GetOk("page"); ok { + urlValues.Add("page", strconv.Itoa(page.(int))) + } + if size, ok := d.GetOk("size"); ok { + urlValues.Add("size", strconv.Itoa(size.(int))) + } + + log.Debugf("utilityAccountListCheckPresence: load account list") + accountListRaw, err := controller.decortAPICall("POST", accountListAPI, urlValues) + if err != nil { + return nil, err + } + + err = json.Unmarshal([]byte(accountListRaw), &accountList) + if err != nil { + return nil, err + } + + return accountList, nil +} + +/*uncomment for cloudbroker func utilityAccountListCheckPresence(d *schema.ResourceData, m interface{}) (AccountList, error) { accountList := AccountList{} controller := m.(*ControllerCfg) @@ -59,3 +86,4 @@ func utilityAccountListCheckPresence(d *schema.ResourceData, m interface{}) (Acc return accountList, nil } +*/ diff --git a/samples/resource_account/main.tf b/samples/resource_account/main.tf new file mode 100644 index 0000000..1b456be --- /dev/null +++ b/samples/resource_account/main.tf @@ -0,0 +1,59 @@ +/* +Пример использования +Ресурса cdrom image +Ресурс позволяет: +1. Создавать образ +2. Редактировать образ +3. Удалять образ + +*/ +#Расскомментируйте этот код, +#и внесите необходимые правки в версию и путь, +#чтобы работать с установленным вручную (не через hashicorp provider registry) провайдером + +terraform { + required_providers { + decort = { + version = "1.1" + source = "digitalenergy.online/decort/decort" + } + } +} + + +provider "decort" { + authenticator = "oauth2" + #controller_url = + controller_url = "https://ds1.digitalenergy.online" + #oauth2_url = + oauth2_url = "https://sso.digitalenergy.online" + allow_unverified_ssl = true +} + +resource "decort_account" "a" { + #id аккаунта + #обязательный параметр + #тип - число + account_id = 11111 + account_name = "new_my_account" + username = "isername@decs3o" + enable = true + send_access_emails = true + /*users { + user_id = "username_2@decs3o" + access_type = "R" + } + users { + user_id = "username_1@decs3o" + access_type = "R" + }*/ + resource_limits { + cu_m = 1024 + } + + +} + +output "test" { + value = decort_account.a +}