alibabacloudstack/service_apsarastack_mongodb.go (521 lines of code) (raw):
package alibabacloudstack
import (
"fmt"
"log"
"regexp"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/dds"
"github.com/aliyun/terraform-provider-alibabacloudstack/alibabacloudstack/connectivity"
"github.com/aliyun/terraform-provider-alibabacloudstack/alibabacloudstack/errmsgs"
)
type MongoDBService struct {
client *connectivity.AlibabacloudStackClient
}
func (s *MongoDBService) DescribeMongoDBInstance(id string) (instance dds.DBInstance, err error) {
request := dds.CreateDescribeDBInstanceAttributeRequest()
s.client.InitRpcRequest(*request.RpcRequest)
request.DBInstanceId = id
raw, err := s.client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.DescribeDBInstanceAttribute(request)
})
bresponse, ok := raw.(*dds.DescribeDBInstanceAttributeResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
if errmsgs.IsExpectedErrors(err, []string{"InvalidDBInstanceId.NotFound"}) {
return instance, errmsgs.WrapErrorf(err, errmsgs.NotFoundMsg, errmsgs.AlibabacloudStackSdkGoERROR)
}
return instance, errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, id, request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
if bresponse == nil || len(bresponse.DBInstances.DBInstance) == 0 {
return instance, errmsgs.WrapErrorf(errmsgs.Error(errmsgs.GetNotFoundMessage("MongoDB Instance", id)), errmsgs.NotFoundMsg, errmsgs.AlibabacloudStackSdkGoERROR)
}
return bresponse.DBInstances.DBInstance[0], nil
}
// WaitForInstance waits for instance to given statusid
func (s *MongoDBService) WaitForMongoDBInstance(instanceId string, status Status, timeout int) error {
deadline := time.Now().Add(time.Duration(timeout) * time.Second)
for {
instance, err := s.DescribeMongoDBInstance(instanceId)
if err != nil {
if errmsgs.NotFoundError(err) {
if status == Deleted {
return nil
}
} else {
return errmsgs.WrapError(err)
}
}
if instance.DBInstanceStatus == string(status) {
return nil
}
if status == Updating {
if instance.DBInstanceStatus == "NodeCreating" ||
instance.DBInstanceStatus == "NodeDeleting" ||
instance.DBInstanceStatus == "DBInstanceClassChanging" {
return nil
}
}
if time.Now().After(deadline) {
return errmsgs.WrapErrorf(err, errmsgs.WaitTimeoutMsg, instanceId, GetFunc(1), timeout, instance.DBInstanceStatus, string(status), errmsgs.ProviderERROR)
}
time.Sleep(DefaultIntervalShort * time.Second)
}
}
func (s *MongoDBService) RdsMongodbDBInstanceStateRefreshFunc(id string, failStates []string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
object, err := s.DescribeMongoDBInstance(id)
if err != nil {
if errmsgs.NotFoundError(err) {
// Set this to nil as if we didn't find anything.
return nil, "", nil
}
return nil, "", errmsgs.WrapError(err)
}
for _, failState := range failStates {
if object.DBInstanceStatus == failState {
return object, object.DBInstanceStatus, errmsgs.WrapError(errmsgs.Error(errmsgs.FailedToReachTargetStatus, object.DBInstanceStatus))
}
}
return object, object.DBInstanceStatus, nil
}
}
func (s *MongoDBService) DescribeMongoDBSecurityIps(instanceId string) (ips []string, err error) {
request := dds.CreateDescribeSecurityIpsRequest()
s.client.InitRpcRequest(*request.RpcRequest)
request.DBInstanceId = instanceId
raw, err := s.client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.DescribeSecurityIps(request)
})
bresponse, ok := raw.(*dds.DescribeSecurityIpsResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return ips, errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, instanceId, request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
var ipstr, separator string
ipsMap := make(map[string]string)
for _, ip := range bresponse.SecurityIpGroups.SecurityIpGroup {
if ip.SecurityIpGroupAttribute == "hidden" {
continue
}
ipstr += separator + ip.SecurityIpList
separator = COMMA_SEPARATED
}
for _, ip := range strings.Split(ipstr, COMMA_SEPARATED) {
ipsMap[ip] = ip
}
var finalIps []string
if len(ipsMap) > 0 {
for key := range ipsMap {
finalIps = append(finalIps, key)
}
}
return finalIps, nil
}
func (s *MongoDBService) ModifyMongoDBSecurityIps(instanceId, ips string) error {
request := dds.CreateModifySecurityIpsRequest()
s.client.InitRpcRequest(*request.RpcRequest)
request.DBInstanceId = instanceId
request.SecurityIps = ips
raw, err := s.client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.ModifySecurityIps(request)
})
bresponse, ok := raw.(*dds.ModifySecurityIpsResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, instanceId, request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
if err := s.WaitForMongoDBInstance(instanceId, Running, DefaultTimeoutMedium); err != nil {
return errmsgs.WrapError(err)
}
return nil
}
func (s *MongoDBService) DescribeMongoDBSecurityGroupId(id string) (*dds.DescribeSecurityGroupConfigurationResponse, error) {
response := &dds.DescribeSecurityGroupConfigurationResponse{}
request := dds.CreateDescribeSecurityGroupConfigurationRequest()
s.client.InitRpcRequest(*request.RpcRequest)
request.DBInstanceId = id
if err := s.WaitForMongoDBInstance(id, Running, DefaultTimeoutMedium); err != nil {
return response, errmsgs.WrapError(err)
}
raw, err := s.client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.DescribeSecurityGroupConfiguration(request)
})
bresponse, ok := raw.(*dds.DescribeSecurityGroupConfigurationResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return response, errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, id, request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
response, _ = raw.(*dds.DescribeSecurityGroupConfigurationResponse)
return response, nil
}
func (server *MongoDBService) ModifyMongodbShardingInstanceNode(
instanceID string, nodeType MongoDBShardingNodeType, stateList, diffList []interface{}) error {
client := server.client
err := server.WaitForMongoDBInstance(instanceID, Running, DefaultLongTimeout)
if err != nil {
return errmsgs.WrapError(err)
}
//create node
if len(stateList) < len(diffList) {
createList := diffList[len(stateList):]
diffList = diffList[:len(stateList)]
for _, item := range createList {
node := item.(map[string]interface{})
request := dds.CreateCreateNodeRequest()
server.client.InitRpcRequest(*request.RpcRequest)
request.DBInstanceId = instanceID
request.NodeClass = node["node_class"].(string)
request.NodeType = string(nodeType)
request.ClientToken = buildClientToken(request.GetActionName())
if nodeType == MongoDBShardingNodeShard {
request.NodeStorage = requests.NewInteger(node["node_storage"].(int))
}
raw, err := client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.CreateNode(request)
})
bresponse, ok := raw.(*dds.CreateNodeResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, instanceID, request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
err = server.WaitForMongoDBInstance(instanceID, Updating, DefaultLongTimeout)
if err != nil {
return errmsgs.WrapError(err)
}
err = server.WaitForMongoDBInstance(instanceID, Running, DefaultLongTimeout)
if err != nil {
return errmsgs.WrapError(err)
}
}
} else if len(stateList) > len(diffList) {
deleteList := stateList[len(diffList):]
stateList = stateList[:len(diffList)]
for _, item := range deleteList {
node := item.(map[string]interface{})
request := dds.CreateDeleteNodeRequest()
server.client.InitRpcRequest(*request.RpcRequest)
request.DBInstanceId = instanceID
request.NodeId = node["node_id"].(string)
request.ClientToken = buildClientToken(request.GetActionName())
raw, err := client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.DeleteNode(request)
})
bresponse, ok := raw.(*dds.DeleteNodeResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, instanceID, request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
err = server.WaitForMongoDBInstance(instanceID, Running, DefaultLongTimeout)
if err != nil {
return errmsgs.WrapError(err)
}
}
}
//modify node
for key := 0; key < len(stateList); key++ {
state := stateList[key].(map[string]interface{})
diff := diffList[key].(map[string]interface{})
if state["node_class"] != diff["node_class"] ||
state["node_storage"] != diff["node_storage"] {
request := dds.CreateModifyNodeSpecRequest()
server.client.InitRpcRequest(*request.RpcRequest)
request.DBInstanceId = instanceID
request.NodeClass = diff["node_class"].(string)
request.ClientToken = buildClientToken(request.GetActionName())
if nodeType == MongoDBShardingNodeShard {
request.NodeStorage = requests.NewInteger(diff["node_storage"].(int))
}
request.NodeId = state["node_id"].(string)
raw, err := client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.ModifyNodeSpec(request)
})
bresponse, ok := raw.(*dds.ModifyNodeSpecResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, instanceID, request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
err = server.WaitForMongoDBInstance(instanceID, Updating, DefaultLongTimeout)
if err != nil {
return errmsgs.WrapError(err)
}
err = server.WaitForMongoDBInstance(instanceID, Running, DefaultLongTimeout)
if err != nil {
return errmsgs.WrapError(err)
}
}
}
return nil
}
func (s *MongoDBService) DescribeMongoDBBackupPolicy(id string) (*dds.DescribeBackupPolicyResponse, error) {
response := &dds.DescribeBackupPolicyResponse{}
request := dds.CreateDescribeBackupPolicyRequest()
s.client.InitRpcRequest(*request.RpcRequest)
request.DBInstanceId = id
raw, err := s.client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.DescribeBackupPolicy(request)
})
bresponse, ok := raw.(*dds.DescribeBackupPolicyResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return response, errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, id, request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
response, _ = raw.(*dds.DescribeBackupPolicyResponse)
return response, nil
}
func (s *MongoDBService) DescribeMongoDBTDEInfo(id string) (*dds.DescribeDBInstanceTDEInfoResponse, error) {
response := &dds.DescribeDBInstanceTDEInfoResponse{}
request := dds.CreateDescribeDBInstanceTDEInfoRequest()
s.client.InitRpcRequest(*request.RpcRequest)
request.DBInstanceId = id
statErr := s.WaitForMongoDBInstance(id, Running, DefaultLongTimeout)
if statErr != nil {
return response, errmsgs.WrapError(statErr)
}
raw, err := s.client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.DescribeDBInstanceTDEInfo(request)
})
bresponse, ok := raw.(*dds.DescribeDBInstanceTDEInfoResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return response, errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, id, request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
response, _ = raw.(*dds.DescribeDBInstanceTDEInfoResponse)
return response, nil
}
func (s *MongoDBService) DescribeDBInstanceSSL(id string) (*dds.DescribeDBInstanceSSLResponse, error) {
response := &dds.DescribeDBInstanceSSLResponse{}
request := dds.CreateDescribeDBInstanceSSLRequest()
err := resource.Retry(10*time.Minute, func() *resource.RetryError {
instance, err := s.DescribeMongoDBInstance(id)
if err != nil {
if errmsgs.IsExpectedErrors(err, []string{"InvalidDBInstanceId.NotFound"}) {
return resource.NonRetryableError(err)
}
return resource.RetryableError(err)
}
if instance.DBInstanceStatus == "SSLModifying" {
return resource.RetryableError(fmt.Errorf("SSLModifying"))
}
addDebug(request.GetActionName(), instance, request.RpcRequest, request)
return nil
})
if err != nil {
return response, errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, id, request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR)
}
s.client.InitRpcRequest(*request.RpcRequest)
request.DBInstanceId = id
raw, err := s.client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.DescribeDBInstanceSSL(request)
})
bresponse, ok := raw.(*dds.DescribeDBInstanceSSLResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return response, errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, id, request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
response, _ = raw.(*dds.DescribeDBInstanceSSLResponse)
return response, nil
}
func (s *MongoDBService) MotifyMongoDBBackupPolicy(d *schema.ResourceData) error {
if err := s.WaitForMongoDBInstance(d.Id(), Running, DefaultTimeoutMedium); err != nil {
return errmsgs.WrapError(err)
}
periodList := expandStringList(connectivity.GetResourceData(d, "preferred_backup_period", "backup_period").(*schema.Set).List())
backupPeriod := fmt.Sprintf("%s", strings.Join(periodList[:], COMMA_SEPARATED))
backupTime := connectivity.GetResourceData(d, "preferred_backup_time", "backup_time").(string)
request := dds.CreateModifyBackupPolicyRequest()
s.client.InitRpcRequest(*request.RpcRequest)
request.DBInstanceId = d.Id()
request.PreferredBackupPeriod = backupPeriod
request.PreferredBackupTime = backupTime
raw, err := s.client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.ModifyBackupPolicy(request)
})
bresponse, ok := raw.(*dds.ModifyBackupPolicyResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
if err := s.WaitForMongoDBInstance(d.Id(), Running, DefaultTimeoutMedium); err != nil {
return errmsgs.WrapError(err)
}
return nil
}
func (s *MongoDBService) ResetAccountPassword(d *schema.ResourceData, password string) error {
request := dds.CreateResetAccountPasswordRequest()
s.client.InitRpcRequest(*request.RpcRequest)
request.DBInstanceId = d.Id()
request.AccountName = "root"
request.AccountPassword = password
raw, err := s.client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.ResetAccountPassword(request)
})
bresponse, ok := raw.(*dds.ResetAccountPasswordResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
return err
}
func (s *MongoDBService) setInstanceTags(d *schema.ResourceData) error {
oraw, nraw := d.GetChange("tags")
o := oraw.(map[string]interface{})
n := nraw.(map[string]interface{})
create, remove := s.diffTags(s.tagsFromMap(o), s.tagsFromMap(n))
if len(remove) > 0 {
var tagKey []string
for _, v := range remove {
tagKey = append(tagKey, v.Key)
}
request := dds.CreateUntagResourcesRequest()
s.client.InitRpcRequest(*request.RpcRequest)
request.ResourceId = &[]string{d.Id()}
request.ResourceType = "INSTANCE"
request.TagKey = &tagKey
raw, err := s.client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.UntagResources(request)
})
bresponse, ok := raw.(*dds.UntagResourcesResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
}
if len(create) > 0 {
request := dds.CreateTagResourcesRequest()
s.client.InitRpcRequest(*request.RpcRequest)
request.ResourceId = &[]string{d.Id()}
request.Tag = &create
request.ResourceType = "INSTANCE"
raw, err := s.client.WithDdsClient(func(client *dds.Client) (interface{}, error) {
return client.TagResources(request)
})
bresponse, ok := raw.(*dds.TagResourcesResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
}
//d.SetPartial("tags")
return nil
}
func (s *MongoDBService) tagsToMap(tags []dds.Tag) map[string]string {
result := make(map[string]string)
for _, t := range tags {
if !s.ignoreTag(t) {
result[t.Key] = t.Value
}
}
return result
}
func (s *MongoDBService) ignoreTag(t dds.Tag) bool {
filter := []string{"^aliyun", "^acs:", "^http://", "^https://"}
for _, v := range filter {
log.Printf("[DEBUG] Matching prefix %v with %v\n", v, t.Key)
ok, _ := regexp.MatchString(v, t.Key)
if ok {
log.Printf("[DEBUG] Found Alibaba Cloud specific t %s (val: %s), ignoring.\n", t.Key, t.Value)
return true
}
}
return false
}
func (s *MongoDBService) tagsInAttributeToMap(tags []dds.Tag) map[string]string {
result := make(map[string]string)
for _, t := range tags {
if !s.ignoreTagInAttribute(t) {
result[t.Key] = t.Value
}
}
return result
}
func (s *MongoDBService) ignoreTagInAttribute(t dds.Tag) bool {
filter := []string{"^aliyun", "^acs:", "^http://", "^https://"}
for _, v := range filter {
log.Printf("[DEBUG] Matching prefix %v with %v\n", v, t.Key)
ok, _ := regexp.MatchString(v, t.Key)
if ok {
log.Printf("[DEBUG] Found Alibaba Cloud specific t %s (val: %s), ignoring.\n", t.Key, t.Value)
return true
}
}
return false
}
func (s *MongoDBService) diffTags(oldTags, newTags []dds.TagResourcesTag) ([]dds.TagResourcesTag, []dds.TagResourcesTag) {
// First, we're creating everything we have
create := make(map[string]interface{})
for _, t := range newTags {
create[t.Key] = t.Value
}
// Build the list of what to remove
var remove []dds.TagResourcesTag
for _, t := range oldTags {
old, ok := create[t.Key]
if !ok || old != t.Value {
// Delete it!
remove = append(remove, t)
}
}
return s.tagsFromMap(create), remove
}
func (s *MongoDBService) tagsFromMap(m map[string]interface{}) []dds.TagResourcesTag {
result := make([]dds.TagResourcesTag, 0, len(m))
for k, v := range m {
result = append(result, dds.TagResourcesTag{
Key: k,
Value: v.(string),
})
}
return result
}