yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aws/dbinstance.go (about) 1 // Copyright 2019 Yunion 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package aws 16 17 import ( 18 "context" 19 "fmt" 20 "strings" 21 "time" 22 23 "yunion.io/x/jsonutils" 24 "yunion.io/x/log" 25 "yunion.io/x/pkg/errors" 26 27 billing "yunion.io/x/cloudmux/pkg/apis/billing" 28 api "yunion.io/x/cloudmux/pkg/apis/compute" 29 "yunion.io/x/cloudmux/pkg/cloudprovider" 30 "yunion.io/x/cloudmux/pkg/multicloud" 31 ) 32 33 type SDBParameterGroup struct { 34 DBParameterGroupName string `xml:"DBParameterGroupName"` 35 ParameterApplyStatus string `xml:"ParameterApplyStatus"` 36 } 37 38 type SOptionGroupMembership struct { 39 OptionGroupName string `xml:"OptionGroupName"` 40 Status string `xml:"Status"` 41 } 42 43 type SEndpoint struct { 44 HostedZoneId string `xml:"HostedZoneId"` 45 Address string `xml:"Address"` 46 Port int `xml:"Port"` 47 } 48 49 type SSubnetAvailabilityZone struct { 50 Name string `xml:"Name"` 51 } 52 53 type SSubnet struct { 54 SubnetIdentifier string `xml:"SubnetIdentifier"` 55 SubnetStatus string `xml:"SubnetStatus"` 56 SubnetAvailabilityZone SSubnetAvailabilityZone `xml:"SubnetAvailabilityZone"` 57 } 58 59 type SDBSubnetGroup struct { 60 VpcId string `xml:"VpcId"` 61 Subnets []SSubnet `xml:"Subnets>Subnet"` 62 SubnetGroupStatus string `xml:"SubnetGroupStatus"` 63 DBSubnetGroupDescription string `xml:"DBSubnetGroupDescription"` 64 DBSubnetGroupName string `xml:"DBSubnetGroupName"` 65 } 66 67 type SVpcSecurityGroupMembership struct { 68 VpcSecurityGroupId string `xml:"VpcSecurityGroupId"` 69 Status string `xml:"Status"` 70 } 71 72 type SVpcSecurityGroups struct { 73 VpcSecurityGroupMembership SVpcSecurityGroupMembership `xml:"VpcSecurityGroupMembership"` 74 } 75 76 type SDBInstance struct { 77 multicloud.SDBInstanceBase 78 AwsTags 79 80 region *SRegion 81 82 AllocatedStorage int `xml:"AllocatedStorage"` 83 //AssociatedRoles string `xml:"AssociatedRoles"` 84 DBParameterGroups []SDBParameterGroup `xml:"DBParameterGroups>DBParameterGroup"` 85 AvailabilityZone string `xml:"AvailabilityZone"` 86 DBSecurityGroups string `xml:"DBSecurityGroups"` 87 EngineVersion string `xml:"EngineVersion"` 88 MasterUsername string `xml:"MasterUsername"` 89 InstanceCreateTime time.Time `xml:"InstanceCreateTime"` 90 DBInstanceClass string `xml:"DBInstanceClass"` 91 HttpEndpointEnabled bool `xml:"HttpEndpointEnabled"` 92 //ReadReplicaDBInstanceIdentifiers string `xml:"ReadReplicaDBInstanceIdentifiers"` 93 MonitoringInterval int `xml:"MonitoringInterval"` 94 DBInstanceStatus string `xml:"DBInstanceStatus"` 95 BackupRetentionPeriod int `xml:"BackupRetentionPeriod"` 96 OptionGroupMemberships []SOptionGroupMembership `xml:"OptionGroupMemberships>OptionGroupMembership"` 97 CACertificateIdentifier string `xml:"CACertificateIdentifier"` 98 DbInstancePort int `xml:"DbInstancePort"` 99 DbiResourceId string `xml:"DbiResourceId"` 100 PreferredBackupWindow string `xml:"PreferredBackupWindow"` 101 DeletionProtection bool `xml:"DeletionProtection"` 102 DBInstanceIdentifier string `xml:"DBInstanceIdentifier"` 103 DBInstanceArn string `xml:"DBInstanceArn"` 104 Endpoint SEndpoint `xml:"Endpoint"` 105 Engine string `xml:"Engine"` 106 PubliclyAccessible bool `xml:"PubliclyAccessible"` 107 IAMDatabaseAuthenticationEnabled bool `xml:"IAMDatabaseAuthenticationEnabled"` 108 PerformanceInsightsEnabled bool `xml:"PerformanceInsightsEnabled"` 109 DBName string `xml:"DBName"` 110 MultiAZ bool `xml:"MultiAZ"` 111 //DomainMemberships string `xml:"DomainMemberships"` 112 StorageEncrypted bool `xml:"StorageEncrypted"` 113 DBSubnetGroup SDBSubnetGroup `xml:"DBSubnetGroup"` 114 VpcSecurityGroups SVpcSecurityGroups `xml:"VpcSecurityGroups"` 115 LicenseModel string `xml:"LicenseModel"` 116 PreferredMaintenanceWindow string `xml:"PreferredMaintenanceWindow"` 117 StorageType string `xml:"StorageType"` 118 AutoMinorVersionUpgrade bool `xml:"AutoMinorVersionUpgrade"` 119 CopyTagsToSnapshot bool `xml:"CopyTagsToSnapshot"` 120 } 121 122 type SDBInstances struct { 123 DBInstances []SDBInstance `xml:"DBInstances>DBInstance"` 124 Marker string `xml:"Marker"` 125 } 126 127 func (rds *SDBInstance) GetName() string { 128 return rds.DBInstanceIdentifier 129 } 130 131 func (rds *SDBInstance) GetId() string { 132 return rds.DbiResourceId 133 } 134 135 func (rds *SDBInstance) GetGlobalId() string { 136 return rds.GetId() 137 } 138 139 // https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/accessing-monitoring.html#Overview.DBInstance.Status 140 func (rds *SDBInstance) GetStatus() string { 141 switch rds.DBInstanceStatus { 142 case "creating", "backing-up": 143 return api.DBINSTANCE_DEPLOYING 144 case "available": 145 return api.DBINSTANCE_RUNNING 146 case "deleting": 147 return api.DBINSTANCE_DELETING 148 case "rebooting": 149 return api.DBINSTANCE_REBOOTING 150 default: 151 log.Errorf("Unknown db instance status: %s", rds.DBInstanceStatus) 152 return api.DBINSTANCE_UNKNOWN 153 } 154 } 155 156 func (rds *SDBInstance) GetBillingType() string { 157 return billing.BILLING_TYPE_POSTPAID 158 } 159 160 func (rds *SDBInstance) GetExpiredAt() time.Time { 161 return time.Time{} 162 } 163 164 func (rds *SDBInstance) GetCreatedAt() time.Time { 165 return rds.InstanceCreateTime 166 } 167 168 func (rds *SDBInstance) Reboot() error { 169 return rds.region.RebootDBInstance(rds.DBInstanceIdentifier) 170 } 171 172 func (self *SDBInstance) GetCategory() string { 173 switch self.Engine { 174 case "aurora", "aurora-mysql": 175 return api.DBINSTANCE_TYPE_MYSQL 176 case "aurora-postgresql": 177 return api.DBINSTANCE_TYPE_POSTGRESQL 178 case "oracle-ee", "sqlserver-ee": 179 return api.AWS_DBINSTANCE_CATEGORY_ENTERPRISE_EDITION 180 case "oracle-se2": 181 return api.AWS_DBINSTANCE_CATEGORY_STANDARD_EDITION_TWO 182 case "sqlserver-se": 183 return api.AWS_DBINSTANCE_CATEGORY_STANDARD_EDITION 184 case "sqlserver-ex": 185 return api.AWS_DBINSTANCE_CATEGORY_EXPRESS_EDITION 186 case "sqlserver-web": 187 return api.AWS_DBINSTANCE_CATEGORY_WEB_EDITION 188 default: 189 if strings.HasPrefix(self.DBInstanceClass, "db.r") || strings.HasPrefix(self.DBInstanceClass, "db.x") || strings.HasPrefix(self.DBInstanceClass, "db.d") { 190 return api.AWS_DBINSTANCE_CATEGORY_MEMORY_OPTIMIZED 191 } 192 return api.AWS_DBINSTANCE_CATEGORY_GENERAL_PURPOSE 193 } 194 } 195 196 func (rds *SDBInstance) GetStorageType() string { 197 return rds.StorageType 198 } 199 200 func (rds *SDBInstance) GetEngine() string { 201 if strings.Contains(rds.Engine, "aurora") { 202 return api.DBINSTANCE_TYPE_AURORA 203 } 204 if strings.Contains(rds.Engine, "oracle") { 205 return api.DBINSTANCE_TYPE_ORACLE 206 } 207 if strings.Contains(rds.Engine, "sqlserver") { 208 return api.DBINSTANCE_TYPE_SQLSERVER 209 } 210 for k, v := range map[string]string{ 211 "mariadb": api.DBINSTANCE_TYPE_MARIADB, 212 "mysql": api.DBINSTANCE_TYPE_MYSQL, 213 "postgres": api.DBINSTANCE_TYPE_POSTGRESQL, 214 } { 215 if rds.Engine == k { 216 return v 217 } 218 } 219 return rds.Engine 220 } 221 222 func (rds *SDBInstance) GetEngineVersion() string { 223 return rds.EngineVersion 224 } 225 226 func (rds *SDBInstance) GetInstanceType() string { 227 return rds.DBInstanceClass 228 } 229 230 func (rds *SDBInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedDBInstanceChangeConfig) error { 231 params := map[string]string{ 232 "DBInstanceIdentifier": rds.DBInstanceIdentifier, 233 "ApplyImmediately": "true", 234 } 235 if config.DiskSizeGB > 0 && rds.GetEngine() != api.DBINSTANCE_TYPE_AURORA { 236 params["AllocatedStorage"] = fmt.Sprintf("%d", config.DiskSizeGB) 237 } 238 if len(config.InstanceType) > 0 { 239 params["DBInstanceClass"] = config.InstanceType 240 } 241 return rds.region.rdsRequest("ModifyDBInstance", params, nil) 242 } 243 244 func (rds *SDBInstance) GetVcpuCount() int { 245 if spec, ok := DBInstanceSpecs[rds.DBInstanceClass]; ok { 246 return spec.VcpuCount 247 } 248 return 0 249 } 250 251 func (rds *SDBInstance) GetVmemSizeMB() int { 252 if spec, ok := DBInstanceSpecs[rds.DBInstanceClass]; ok { 253 return spec.VmemSizeMb 254 } 255 return 0 256 } 257 258 func (rds *SDBInstance) GetDiskSizeGB() int { 259 return rds.AllocatedStorage 260 } 261 262 func (rds *SDBInstance) GetPort() int { 263 return rds.Endpoint.Port 264 } 265 266 func (rds *SDBInstance) GetMaintainTime() string { 267 return rds.PreferredMaintenanceWindow 268 } 269 270 func (rds *SDBInstance) GetIVpcId() string { 271 return rds.DBSubnetGroup.VpcId 272 } 273 274 func (rds *SDBInstance) Refresh() error { 275 instance, err := rds.region.GetDBInstance(rds.DbiResourceId) 276 if err != nil { 277 return err 278 } 279 rds.AwsTags = instance.AwsTags 280 return jsonutils.Update(rds, instance) 281 } 282 283 func (region *SRegion) GetDBInstance(instanceId string) (*SDBInstance, error) { 284 instances, _, err := region.GetDBInstances(instanceId, "") 285 if err != nil { 286 return nil, errors.Wrap(err, "GetDBInstances") 287 } 288 289 if len(instances) == 1 { 290 if instances[0].DbiResourceId == instanceId { 291 instances[0].region = region 292 return &instances[0], nil 293 } 294 return nil, cloudprovider.ErrNotFound 295 } 296 297 if len(instances) == 0 { 298 return nil, cloudprovider.ErrNotFound 299 } 300 301 return nil, cloudprovider.ErrDuplicateId 302 } 303 304 func (rds *SDBInstance) GetZone1Id() string { 305 if len(rds.AvailabilityZone) > 0 { 306 zone, err := rds.region.getZoneById(rds.AvailabilityZone) 307 if err != nil { 308 log.Errorf("rds.GetIZoneId %s error: %v", rds.DBInstanceIdentifier, err) 309 return "" 310 } 311 return zone.GetGlobalId() 312 } 313 return "" 314 } 315 316 func (rds *SDBInstance) GetZone2Id() string { 317 return "" 318 } 319 320 func (rds *SDBInstance) GetZone3Id() string { 321 return "" 322 } 323 324 func (rds *SDBInstance) GetIDBInstanceAccounts() ([]cloudprovider.ICloudDBInstanceAccount, error) { 325 accounts := []cloudprovider.ICloudDBInstanceAccount{} 326 if len(rds.MasterUsername) > 0 { 327 account := &SDBInstanceAccount{instance: rds, AccountName: rds.MasterUsername} 328 accounts = append(accounts, account) 329 } 330 return accounts, nil 331 } 332 333 func (rds *SDBInstance) GetDBNetworks() ([]cloudprovider.SDBInstanceNetwork, error) { 334 return []cloudprovider.SDBInstanceNetwork{}, nil 335 } 336 337 func (rds *SDBInstance) GetInternalConnectionStr() string { 338 return rds.Endpoint.Address 339 } 340 341 func (rds *SDBInstance) GetConnectionStr() string { 342 if rds.PubliclyAccessible { 343 return rds.Endpoint.Address 344 } 345 return "" 346 } 347 348 func (rds *SDBInstance) OpenPublicConnection() error { 349 params := map[string]string{ 350 "DBInstanceIdentifier": rds.DBInstanceIdentifier, 351 "PubliclyAccessible": "true", 352 } 353 return rds.region.rdsRequest("ModifyDBInstance", params, nil) 354 } 355 356 func (rds *SDBInstance) ClosePublicConnection() error { 357 params := map[string]string{ 358 "DBInstanceIdentifier": rds.DBInstanceIdentifier, 359 "PubliclyAccessible": "false", 360 } 361 return rds.region.rdsRequest("ModifyDBInstance", params, nil) 362 } 363 364 func (rds *SDBInstance) GetIDBInstanceParameters() ([]cloudprovider.ICloudDBInstanceParameter, error) { 365 parameters, err := rds.region.GetDBInstanceParameters(rds.DBParameterGroups[0].DBParameterGroupName) 366 if err != nil { 367 return nil, errors.Wrap(err, "GetDBInstanceParameters") 368 } 369 iparams := []cloudprovider.ICloudDBInstanceParameter{} 370 for i := 0; i < len(parameters); i++ { 371 parameters[i].instance = rds 372 iparams = append(iparams, ¶meters[i]) 373 } 374 return iparams, nil 375 } 376 377 func (rds *SDBInstance) GetIDBInstanceDatabases() ([]cloudprovider.ICloudDBInstanceDatabase, error) { 378 idatabases := []cloudprovider.ICloudDBInstanceDatabase{} 379 if len(rds.DBName) > 0 { 380 database := &SDBInstanceDatabase{DBName: rds.DBName} 381 idatabases = append(idatabases, database) 382 } 383 return idatabases, nil 384 } 385 386 func (rds *SDBInstance) GetIDBInstanceBackups() ([]cloudprovider.ICloudDBInstanceBackup, error) { 387 backups, err := rds.region.GetDBInstanceSnapshots(rds.DBInstanceIdentifier, "") 388 if err != nil { 389 return nil, err 390 } 391 ret := []cloudprovider.ICloudDBInstanceBackup{} 392 for i := range backups { 393 backups[i].region = rds.region 394 ret = append(ret, &backups[i]) 395 } 396 return ret, nil 397 } 398 399 func (rds *SDBInstance) CreateIBackup(conf *cloudprovider.SDBInstanceBackupCreateConfig) (string, error) { 400 params := map[string]string{ 401 "DBInstanceIdentifier": rds.DBInstanceIdentifier, 402 "DBSnapshotIdentifier": conf.Name, 403 } 404 ret := struct { 405 DBSnapshot SDBInstanceSnapshot `xml:"DBSnapshot"` 406 }{} 407 err := rds.region.rdsRequest("CreateDBSnapshot", params, &ret) 408 if err != nil { 409 return "", err 410 } 411 ret.DBSnapshot.region = rds.region 412 cloudprovider.WaitStatus(&ret.DBSnapshot, api.DBINSTANCE_BACKUP_READY, time.Second*10, time.Hour*2) 413 return ret.DBSnapshot.GetGlobalId(), nil 414 } 415 416 func (region *SRegion) GetDBInstances(instanceId, marker string) ([]SDBInstance, string, error) { 417 instances := SDBInstances{} 418 params := map[string]string{} 419 idx := 1 420 if len(instanceId) > 0 { 421 params[fmt.Sprintf("Filters.Filter.%d.Name", idx)] = "dbi-resource-id" 422 params[fmt.Sprintf("Filters.Filter.%d.Values.Value.1", idx)] = instanceId 423 } 424 425 if len(marker) > 0 { 426 params["Marker"] = marker 427 } 428 429 err := region.rdsRequest("DescribeDBInstances", params, &instances) 430 if err != nil { 431 return nil, "", errors.Wrap(err, "DescribeDBInstances") 432 } 433 return instances.DBInstances, instances.Marker, nil 434 } 435 436 func (region *SRegion) GetIDBInstances() ([]cloudprovider.ICloudDBInstance, error) { 437 idbinstances := []cloudprovider.ICloudDBInstance{} 438 instances, marker, err := region.GetDBInstances("", "") 439 if err != nil { 440 return nil, errors.Wrap(err, "GetDBInstances") 441 } 442 for i := 0; i < len(instances); i++ { 443 instances[i].region = region 444 idbinstances = append(idbinstances, &instances[i]) 445 } 446 for len(marker) > 0 { 447 instances, marker, err = region.GetDBInstances("", marker) 448 if err != nil { 449 return nil, errors.Wrap(err, "GetDBInstances") 450 } 451 for i := 0; i < len(instances); i++ { 452 instances[i].region = region 453 idbinstances = append(idbinstances, &instances[i]) 454 } 455 } 456 return idbinstances, nil 457 } 458 459 func (self *SRegion) GetIDBInstanceById(id string) (cloudprovider.ICloudDBInstance, error) { 460 instances, _, err := self.GetDBInstances(id, "") 461 if err != nil { 462 return nil, errors.Wrap(err, "GetDBInstances") 463 } 464 465 if len(instances) > 1 { 466 return nil, errors.Wrapf(cloudprovider.ErrDuplicateId, id) 467 } 468 469 if len(instances) == 0 { 470 return nil, errors.Wrapf(cloudprovider.ErrNotFound, id) 471 } 472 473 instances[0].region = self 474 return &instances[0], nil 475 } 476 477 func (self *SRegion) CreateIDBInstance(desc *cloudprovider.SManagedDBInstanceCreateConfig) (cloudprovider.ICloudDBInstance, error) { 478 params := map[string]string{ 479 "DBInstanceClass": desc.InstanceType, 480 "DBInstanceIdentifier": desc.Name, 481 "EngineVersion": desc.EngineVersion, 482 "MasterUsername": "admin", 483 } 484 if len(desc.Password) > 0 { 485 params["MasterUserPassword"] = desc.Password 486 } 487 488 if desc.Engine != api.DBINSTANCE_TYPE_AURORA { 489 params["StorageType"] = desc.StorageType 490 params["AllocatedStorage"] = fmt.Sprintf("%d", desc.DiskSizeGB) 491 for i, sec := range desc.SecgroupIds { 492 params[fmt.Sprintf("VpcSecurityGroupIds.VpcSecurityGroupId.%d", i+1)] = sec 493 } 494 } 495 if desc.MultiAz { 496 params["MultiAZ"] = "true" 497 } 498 if desc.StorageType == api.STORAGE_IO1_SSD { 499 params["Iops"] = "3000" 500 } 501 switch desc.Engine { 502 case api.DBINSTANCE_TYPE_MYSQL: 503 params["Engine"] = "mysql" 504 case api.DBINSTANCE_TYPE_POSTGRESQL: 505 params["Engine"] = "postgres" 506 params["MasterUsername"] = "postgres" 507 case api.DBINSTANCE_TYPE_MARIADB: 508 params["Engine"] = "mariadb" 509 case api.DBINSTANCE_TYPE_SQLSERVER: 510 params["LicenseModel"] = "license-included" 511 switch desc.Category { 512 case api.AWS_DBINSTANCE_CATEGORY_ENTERPRISE_EDITION: 513 params["Engine"] = "sqlserver-ee" 514 case api.AWS_DBINSTANCE_CATEGORY_EXPRESS_EDITION: 515 params["Engine"] = "sqlserver-ex" 516 params["MultiAZ"] = "false" 517 case api.AWS_DBINSTANCE_CATEGORY_STANDARD_EDITION: 518 params["Engine"] = "sqlserver-se" 519 case api.AWS_DBINSTANCE_CATEGORY_WEB_EDITION: 520 params["Engine"] = "sqlserver-web" 521 params["MultiAZ"] = "false" 522 default: 523 return nil, fmt.Errorf("invalid category %s for engine %s", desc.Category, desc.Engine) 524 } 525 case api.DBINSTANCE_TYPE_AURORA: 526 delete(params, "MultiAZ") 527 switch desc.Category { 528 case api.DBINSTANCE_TYPE_MYSQL: 529 params["Engine"] = "aurora" 530 if !strings.HasPrefix(desc.EngineVersion, "5.6") { 531 params["Engine"] = "aurora-mysql" 532 } 533 case api.DBINSTANCE_TYPE_POSTGRESQL: 534 params["Engine"] = "aurora-postgresql" 535 params["MasterUsername"] = "postgres" 536 default: 537 return nil, fmt.Errorf("invalid category %s for engine %s", desc.Category, desc.Engine) 538 } 539 case api.DBINSTANCE_TYPE_ORACLE: 540 params["LicenseModel"] = "bring-your-own-license" 541 switch desc.Category { 542 case api.AWS_DBINSTANCE_CATEGORY_ENTERPRISE_EDITION: 543 params["Engine"] = "oracle-ee" 544 case api.AWS_DBINSTANCE_CATEGORY_STANDARD_EDITION_TWO: 545 params["Engine"] = "oracle-se2" 546 default: 547 return nil, fmt.Errorf("invalid category %s for engine %s", desc.Category, desc.Engine) 548 } 549 } 550 i := 1 551 for k, v := range desc.Tags { 552 params[fmt.Sprintf("Tags.Tag.%d.Key", i)] = k 553 params[fmt.Sprintf("Tags.Tag.%d.Value", i)] = v 554 i++ 555 } 556 result := struct { 557 DBInstance SDBInstance `xml:"DBInstance"` 558 }{} 559 result.DBInstance.region = self 560 return &result.DBInstance, self.rdsRequest("CreateDBInstance", params, &result) 561 } 562 563 func (self *SDBInstance) Delete() error { 564 params := map[string]string{ 565 "DBInstanceIdentifier": self.DBInstanceIdentifier, 566 "SkipFinalSnapshot": "true", 567 } 568 return self.region.rdsRequest("DeleteDBInstance", params, nil) 569 } 570 571 func (self *SRegion) RebootDBInstance(id string) error { 572 params := map[string]string{ 573 "DBInstanceIdentifier": id, 574 } 575 return self.rdsRequest("RebootDBInstance", params, nil) 576 } 577 578 func (self *SDBInstance) SetTags(tags map[string]string, replace bool) error { 579 oldTags, err := self.region.ListRdsResourceTags(self.DBInstanceArn) 580 if err != nil { 581 return errors.Wrapf(err, "ListRdsResourceTags") 582 } 583 added, removed := map[string]string{}, map[string]string{} 584 for k, v := range tags { 585 oldValue, ok := oldTags[k] 586 if !ok { 587 added[k] = v 588 } else if oldValue != v { 589 removed[k] = oldValue 590 added[k] = v 591 } 592 } 593 if replace { 594 for k, v := range oldTags { 595 newValue, ok := tags[k] 596 if !ok { 597 removed[k] = v 598 } else if v != newValue { 599 added[k] = newValue 600 removed[k] = v 601 } 602 } 603 } 604 if len(removed) > 0 { 605 err = self.region.RemoveRdsTagsFromResource(self.DBInstanceArn, removed) 606 if err != nil { 607 return errors.Wrapf(err, "RemoveRdsTagsFromResource %s", removed) 608 } 609 } 610 if len(added) > 0 { 611 return self.region.AddRdsTagsToResource(self.DBInstanceArn, added) 612 } 613 return nil 614 } 615 616 func (self *SRegion) ListRdsResourceTags(arn string) (map[string]string, error) { 617 params := map[string]string{ 618 "ResourceName": arn, 619 } 620 tags := AwsTags{} 621 err := self.rdsRequest("ListTagsForResource", params, &tags) 622 if err != nil { 623 return nil, errors.Wrapf(err, "ListTagsForResource") 624 } 625 return tags.GetTags() 626 } 627 628 func (self *SRegion) AddRdsTagsToResource(arn string, tags map[string]string) error { 629 if len(tags) == 0 { 630 return nil 631 } 632 params := map[string]string{ 633 "ResourceName": arn, 634 } 635 i := 1 636 for k, v := range tags { 637 params[fmt.Sprintf("Tags.member.%d.Key", i)] = k 638 params[fmt.Sprintf("Tags.member.%d.Value", i)] = v 639 } 640 return self.rdsRequest("AddTagsToResource", params, nil) 641 } 642 643 func (self *SRegion) RemoveRdsTagsFromResource(arn string, tags map[string]string) error { 644 if len(tags) == 0 { 645 return nil 646 } 647 params := map[string]string{ 648 "ResourceName": arn, 649 } 650 i := 1 651 for k := range tags { 652 params[fmt.Sprintf("TagKeys.member.%d", i)] = k 653 } 654 return self.rdsRequest("RemoveTagsFromResource", params, nil) 655 }