Skip to content

Commit

Permalink
Minimum validation for multitenant formations (IBM-Cloud#5199)
Browse files Browse the repository at this point in the history
* added minimum-validation logic
* refactored minimum compute code
* updated tests to pass with enforced minimums
* used cloud-databases-go-sdk version 0.6.0
* added getCpuEnforcementRatios to get use the cpu enforcement ratios in all the different scenarios
* editted validateMultitenant Function to match CLI and Provisioning-ui
* made changes requested in latest PR review
  • Loading branch information
omaraibrahim authored and ismirlia committed Apr 11, 2024
1 parent 63cce45 commit 237da3c
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 80 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/IBM/apigateway-go-sdk v0.0.0-20210714141226-a5d5d49caaca
github.com/IBM/appconfiguration-go-admin-sdk v0.3.0
github.com/IBM/appid-management-go-sdk v0.0.0-20210908164609-dd0e0eaf732f
github.com/IBM/cloud-databases-go-sdk v0.5.0
github.com/IBM/cloud-databases-go-sdk v0.6.0
github.com/IBM/cloudant-go-sdk v0.0.43
github.com/IBM/code-engine-go-sdk v0.0.0-20231106200405-99e81b3ee752
github.com/IBM/container-registry-go-sdk v1.1.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ github.com/IBM/appid-management-go-sdk v0.0.0-20210908164609-dd0e0eaf732f h1:4c1
github.com/IBM/appid-management-go-sdk v0.0.0-20210908164609-dd0e0eaf732f/go.mod h1:d22kTYY7RYBWcQlZpqrSdshpB/lJ16viWS5Sbjtlc8s=
github.com/IBM/cloud-databases-go-sdk v0.5.0 h1:Bie6MnT1jLchQmtKVA20HHETTPdlOR+i11P2kJ55viM=
github.com/IBM/cloud-databases-go-sdk v0.5.0/go.mod h1:nCIVfeZnhBYIiwByT959dFP4VWUeNLxomDYy63tTC6M=
github.com/IBM/cloud-databases-go-sdk v0.6.0 h1:QK3eif7+kusgeuMB54Zw5nco/kDwsDg2sD/84/foDxo=
github.com/IBM/cloud-databases-go-sdk v0.6.0/go.mod h1:nCIVfeZnhBYIiwByT959dFP4VWUeNLxomDYy63tTC6M=
github.com/IBM/cloudant-go-sdk v0.0.43 h1:YxTy4RpAEezX32YIWnds76hrBREmO4u6IkBz1WylNuQ=
github.com/IBM/cloudant-go-sdk v0.0.43/go.mod h1:WeYrJPaHTw19943ndWnVfwMIlZ5z0XUM2uEXNBrwZ1M=
github.com/IBM/code-engine-go-sdk v0.0.0-20231106200405-99e81b3ee752 h1:S5NT0aKKUqd9hnIrPN/qUijKx9cZjJi3kfFpog0ByDA=
Expand Down
156 changes: 132 additions & 24 deletions ibm/service/database/resource_ibm_database.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,16 @@ func ResourceIBMDatabaseInstance() *schema.Resource {
Computed: true,
Description: "Can memory scale down as well as up.",
},
"cpu_enforcement_ratio_ceiling_mb": {
Type: schema.TypeInt,
Optional: true,
Description: "The amount of memory required before the cpu/memory ratio is no longer enforced. (multitenant only).",
},
"cpu_enforcement_ratio_mb": {
Type: schema.TypeInt,
Optional: true,
Description: "The maximum memory allowed per CPU until the ratio ceiling is reached. (multitenant only).",
},
},
},
},
Expand Down Expand Up @@ -987,21 +997,23 @@ type Group struct {
}

type GroupResource struct {
Units string
Allocation int
Minimum int
Maximum int
StepSize int
IsAdjustable bool
IsOptional bool
CanScaleDown bool
Units string
Allocation int
Minimum int
Maximum int
StepSize int
IsAdjustable bool
IsOptional bool
CanScaleDown bool
CPUEnforcementRatioCeilingMb int
CPUEnforcementRatioMb int
}

type HostFlavorGroupResource struct {
ID string
}

func getDefaultScalingGroups(_service string, _plan string, meta interface{}) (groups []clouddatabasesv5.Group, err error) {
func getDefaultScalingGroups(_service string, _plan string, _hostFlavor string, meta interface{}) (groups []clouddatabasesv5.Group, err error) {
cloudDatabasesClient, err := meta.(conns.ClientSession).CloudDatabasesV5()
if err != nil {
return groups, fmt.Errorf("[ERROR] Error getting database client settings: %s", err)
Expand Down Expand Up @@ -1033,6 +1045,9 @@ func getDefaultScalingGroups(_service string, _plan string, meta interface{}) (g
}

getDefaultScalingGroupsOptions := cloudDatabasesClient.NewGetDefaultScalingGroupsOptions(service)
if _hostFlavor != "" {
getDefaultScalingGroupsOptions.SetHostFlavor(_hostFlavor)
}

getDefaultScalingGroupsResponse, _, err := cloudDatabasesClient.GetDefaultScalingGroups(getDefaultScalingGroupsOptions)
if err != nil {
Expand All @@ -1043,7 +1058,7 @@ func getDefaultScalingGroups(_service string, _plan string, meta interface{}) (g
}

func getInitialNodeCount(service string, plan string, meta interface{}) (int, error) {
groups, err := getDefaultScalingGroups(service, plan, meta)
groups, err := getDefaultScalingGroups(service, plan, "", meta)

if err != nil {
return 0, err
Expand Down Expand Up @@ -2702,14 +2717,16 @@ func normalizeGroups(_groups []clouddatabasesv5.Group) (groups []Group) {
}

group.Memory = &GroupResource{
Units: *g.Memory.Units,
Allocation: int(*g.Memory.AllocationMb),
Minimum: int(*g.Memory.MinimumMb),
Maximum: int(*g.Memory.MaximumMb),
StepSize: int(*g.Memory.StepSizeMb),
IsAdjustable: *g.Memory.IsAdjustable,
IsOptional: *g.Memory.IsOptional,
CanScaleDown: *g.Memory.CanScaleDown,
Units: *g.Memory.Units,
Allocation: int(*g.Memory.AllocationMb),
Minimum: int(*g.Memory.MinimumMb),
Maximum: int(*g.Memory.MaximumMb),
StepSize: int(*g.Memory.StepSizeMb),
IsAdjustable: *g.Memory.IsAdjustable,
IsOptional: *g.Memory.IsOptional,
CanScaleDown: *g.Memory.CanScaleDown,
CPUEnforcementRatioCeilingMb: getCPUEnforcementRatioCeilingMb(g.Memory),
CPUEnforcementRatioMb: getCPUEnforcementRatioMb(g.Memory),
}

group.Disk = &GroupResource{
Expand Down Expand Up @@ -2745,6 +2762,22 @@ func normalizeGroups(_groups []clouddatabasesv5.Group) (groups []Group) {
return groups
}

func getCPUEnforcementRatioCeilingMb(groupMemory *clouddatabasesv5.GroupMemory) int {
if groupMemory.CPUEnforcementRatioCeilingMb != nil {
return int(*groupMemory.CPUEnforcementRatioCeilingMb)
}

return 0
}

func getCPUEnforcementRatioMb(groupMemory *clouddatabasesv5.GroupMemory) int {
if groupMemory.CPUEnforcementRatioMb != nil {
return int(*groupMemory.CPUEnforcementRatioMb)
}

return 0
}

func expandGroups(_groups []interface{}) []*Group {
if len(_groups) == 0 {
return nil
Expand Down Expand Up @@ -2837,6 +2870,21 @@ func validateGroupHostFlavor(groupId string, resourceName string, group *Group)
return nil
}

func validateMultitenantMemoryCpu(resourceDefaults *Group, group *Group, cpuEnforcementRatioCeiling int, cpuEnforcementRatioMb int) error {
// TODO: Replace this with cpuEnforcementRatioCeiling when it is fixed
cpuEnforcementRatioCeilingTemp := 16384

if group.CPU == nil || group.CPU.Allocation == 0 {
return nil
}

if group.CPU.Allocation >= cpuEnforcementRatioCeilingTemp/cpuEnforcementRatioMb {
return nil
} else {
return fmt.Errorf("The current cpu alloaction of %d is not valid for your current configuration.", group.CPU.Allocation)
}
}

func validateGroupsDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) (err error) {
instanceID := diff.Id()
service := diff.Get("service").(string)
Expand All @@ -2846,11 +2894,23 @@ func validateGroupsDiff(_ context.Context, diff *schema.ResourceDiff, meta inter
var currentGroups []Group
var groupList []clouddatabasesv5.Group
var groupIds []string
groups := expandGroups(group.(*schema.Set).List())
var memberGroup *Group
for _, g := range groups {
if g.ID == "member" {
memberGroup = g
break
}
}

if instanceID != "" {
groupList, err = getGroups(instanceID, meta)
} else {
groupList, err = getDefaultScalingGroups(service, plan, meta)
if memberGroup.HostFlavor != nil {
groupList, err = getDefaultScalingGroups(service, plan, memberGroup.HostFlavor.ID, meta)
} else {
groupList, err = getDefaultScalingGroups(service, plan, "", meta)
}
}

if err != nil {
Expand All @@ -2861,6 +2921,12 @@ func validateGroupsDiff(_ context.Context, diff *schema.ResourceDiff, meta inter

tfGroups := expandGroups(group.(*schema.Set).List())

err, cpuEnforcementRatioCeiling, cpuEnforcementRatioMb := getCpuEnforcementRatios(service, plan, meta, group)

if err != nil {
return err
}

// validate group_ids are unique
groupIds = make([]string, 0, len(tfGroups))
for _, g := range tfGroups {
Expand Down Expand Up @@ -2892,15 +2958,15 @@ func validateGroupsDiff(_ context.Context, diff *schema.ResourceDiff, meta inter
// set current nodeCount
nodeCount := groupDefaults.Members.Allocation

if group.Members != nil {
err = validateGroupScaling(groupId, "members", group.Members.Allocation, groupDefaults.Members, 1)
if group.HostFlavor != nil && group.HostFlavor.ID != "" && group.HostFlavor.ID != "multitenant" {
err = validateGroupHostFlavor(groupId, "host_flavor", group)
if err != nil {
return err
}
}

if group.HostFlavor != nil && group.HostFlavor.ID != "" && group.HostFlavor.ID != "multitenant" {
err = validateGroupHostFlavor(groupId, "host_flavor", group)
if group.Members != nil {
err = validateGroupScaling(groupId, "members", group.Members.Allocation, groupDefaults.Members, 1)
if err != nil {
return err
}
Expand All @@ -2911,6 +2977,13 @@ func validateGroupsDiff(_ context.Context, diff *schema.ResourceDiff, meta inter
if err != nil {
return err
}

if group.HostFlavor != nil && group.HostFlavor.ID != "" && group.HostFlavor.ID == "multitenant" && cpuEnforcementRatioCeiling != 0 && cpuEnforcementRatioMb != 0 {
err = validateMultitenantMemoryCpu(groupDefaults, group, cpuEnforcementRatioCeiling, cpuEnforcementRatioMb)
if err != nil {
return err
}
}
}

if group.Disk != nil {
Expand All @@ -2932,6 +3005,41 @@ func validateGroupsDiff(_ context.Context, diff *schema.ResourceDiff, meta inter
return nil
}

func getCpuEnforcementRatios(service string, plan string, meta interface{}, group interface{}) (err error, cpuEnforcementRatioCeiling int, cpuEnforcementRatioMb int) {
var currentGroups []Group
defaultList, err := getDefaultScalingGroups(service, plan, "multitenant", meta)

if err != nil {
return err, 0, 0
}

currentGroups = normalizeGroups(defaultList)
tfGroups := expandGroups(group.(*schema.Set).List())

// Get default or current group scaling values
for _, group := range tfGroups {
if group == nil {
continue
}

groupId := group.ID
var groupDefaults *Group
for _, g := range currentGroups {
if g.ID == groupId {
groupDefaults = &g
break
}
}

if groupDefaults.Memory != nil {
return nil, groupDefaults.Memory.CPUEnforcementRatioCeilingMb, groupDefaults.Memory.CPUEnforcementRatioMb
}

}

return nil, 0, 0
}

func validateUsersDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) (err error) {
service := diff.Get("service").(string)

Expand Down Expand Up @@ -2972,7 +3080,7 @@ func validateUsersDiff(_ context.Context, diff *schema.ResourceDiff, meta interf

// TODO: Use Capability API
// RBAC roles supported for Redis 6.0 and above
if service == "databases-for-redis" && !(version > 0 && version < 6) {
if (service == "databases-for-redis") && !(version > 0 && version < 6) {
err = change.New.ValidateRBACRole()
} else if service == "databases-for-mongodb" && change.New.Type == "ops_manager" {
err = change.New.ValidateOpsManagerRole()
Expand Down

0 comments on commit 237da3c

Please sign in to comment.