Skip to content

Commit 54d9457

Browse files
authored
feat: activity tracker region check and parallel test improvement (#144)
1 parent 7986a81 commit 54d9457

13 files changed

+521
-115
lines changed

.secrets.baseline

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": "go.sum|package-lock.json|^.secrets.baseline$",
44
"lines": null
55
},
6-
"generated_at": "2022-10-20T23:05:05Z",
6+
"generated_at": "2022-10-24T12:33:25Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -82,15 +82,15 @@
8282
"hashed_secret": "892bd503fb45f6fcafb1c7003d88291fc0b20208",
8383
"is_secret": false,
8484
"is_verified": false,
85-
"line_number": 102,
85+
"line_number": 104,
8686
"type": "Secret Keyword",
8787
"verified_result": null
8888
},
8989
{
9090
"hashed_secret": "d4c3d66fd0c38547a3c7a4c6bdc29c36911bc030",
9191
"is_secret": false,
9292
"is_verified": false,
93-
"line_number": 163,
93+
"line_number": 165,
9494
"type": "Secret Keyword",
9595
"verified_result": null
9696
}
@@ -106,7 +106,7 @@
106106
}
107107
]
108108
},
109-
"version": "0.13.1+ibm.54.dss",
109+
"version": "0.13.1+ibm.52.dss",
110110
"word_list": {
111111
"file": null,
112112
"hash": null

cloudinfo/powersystems.go

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
ibmpiinstance "github.com/IBM-Cloud/power-go-client/clients/instance"
1010
"github.com/IBM-Cloud/power-go-client/ibmpisession"
1111
ibmpimodels "github.com/IBM-Cloud/power-go-client/power/models"
12-
"github.com/IBM/go-sdk-core/v5/core"
1312
"github.com/IBM/platform-services-go-sdk/resourcecontrollerv2"
1413
)
1514

@@ -24,48 +23,7 @@ type PowerCloudConnectionDetail struct {
2423
// This function will loop through ALL resources of type "service_instance" in the account
2524
// and then filter by looking for "power-iaas" in the CRN.
2625
func (infoSvc *CloudInfoService) ListPowerWorkspaces() ([]resourcecontrollerv2.ResourceInstance, error) {
27-
28-
listOptions := infoSvc.resourceControllerService.NewListResourceInstancesOptions()
29-
listOptions.SetType("service_instance")
30-
listOptions.SetLimit(int64(100))
31-
32-
// this API is paginated, but there is no pager support in the library at this time.
33-
// we are compensating by inspecting the NextURL and Start values supplied by the API
34-
// to keep looping through pages
35-
maxPages := 100
36-
countPages := 0
37-
listPowerInstance := []resourcecontrollerv2.ResourceInstance{}
38-
moreData := true
39-
40-
for moreData {
41-
listPage, _, err := infoSvc.resourceControllerService.ListResourceInstances(listOptions)
42-
if err != nil {
43-
return nil, fmt.Errorf("error listing PowerVS instances: %w", err)
44-
}
45-
countPages += 1
46-
47-
// search through instances on current page looking for only power-iaas
48-
for _, svcInstance := range listPage.Resources {
49-
if strings.Contains(*svcInstance.CRN, "power-iaas") {
50-
listPowerInstance = append(listPowerInstance, svcInstance)
51-
}
52-
}
53-
54-
// get next page of results if necessary
55-
// see: https://cloud.ibm.com/apidocs/resource-controller/resource-controller?code=go#list-resource-instances
56-
if (countPages < maxPages) && listPage.NextURL != nil && *listPage.NextURL != "" {
57-
moreData = true
58-
newStart, errNext := core.GetQueryParam(listPage.NextURL, "start")
59-
if errNext != nil || newStart == nil {
60-
return nil, fmt.Errorf("error in fetching start value from next_url: %w", errNext)
61-
}
62-
listOptions.SetStart(*newStart)
63-
} else {
64-
moreData = false
65-
}
66-
}
67-
68-
return listPowerInstance, nil
26+
return infoSvc.ListResourcesByCrnServiceName("power-iaas")
6927
}
7028

7129
// ListPowerWorkspaceConnections will return an array of CloudConnection for a given existing connection client

cloudinfo/region.go

Lines changed: 119 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"os"
77
"sort"
88

9+
"github.com/IBM/platform-services-go-sdk/resourcecontrollerv2"
910
"github.com/IBM/vpc-go-sdk/vpcv1"
1011
"gopkg.in/yaml.v3"
1112
)
@@ -15,6 +16,11 @@ const (
1516
maxPowerConnectionsPerZone = 2
1617
)
1718

19+
type GetTestRegionOptions struct {
20+
// exclude a region if it contains an Activity Tracker
21+
ExcludeActivityTrackerRegions bool
22+
}
23+
1824
// GetAvailableVpcRegions is a method for receiver CloudInfoService that will query the caller account
1925
// for all regions that support the VPC resource type and are available in the account.
2026
// Returns an array of vpcv1.Region and error.
@@ -44,12 +50,34 @@ func (infoSvc *CloudInfoService) GetAvailableVpcRegions() ([]vpcv1.Region, error
4450
}
4551

4652
// GetLeastVpcTestRegion is a method for receiver CloudInfoService that will determine a region available
53+
// to the caller account that currently contains the least amount of deployed VPCs, using default options.
54+
// Returns a string representing an IBM Cloud region name, and error.
55+
func (infoSvc *CloudInfoService) GetLeastVpcTestRegion() (string, error) {
56+
// Set up default
57+
options := NewGetTestRegionOptions()
58+
return infoSvc.GetLeastVpcTestRegionO(*options)
59+
}
60+
61+
// GetLeastVpcTestRegionWithoutActivityTracker is a method for receiver CloudInfoService that will determine a
62+
// region available to the caller account that currently contains the least amount of deployed VPCs and does
63+
// not currently contain an active Activity Tracker instance (can only have one per region).
64+
// Returns a string representing an IBM Cloud region name, and error.
65+
func (infoSvc *CloudInfoService) GetLeastVpcTestRegionWithoutActivityTracker() (string, error) {
66+
// get default options
67+
options := NewGetTestRegionOptions()
68+
// change activity tracker setting
69+
options.ExcludeActivityTrackerRegions = true
70+
71+
return infoSvc.GetLeastVpcTestRegionO(*options)
72+
}
73+
74+
// GetLeastVpcTestRegionO is a method for receiver CloudInfoService that will determine a region available
4775
// to the caller account that currently contains the least amount of deployed VPCs.
48-
// The determination can be influenced by specifying CloudInfoService.regionsData.
76+
// The determination can be influenced by specifying CloudInfoService.regionsData and supplying appropriate options.
4977
// If no CloudInfoService.regionsData exists, it will simply loop through all available regions for the caller account
5078
// and choose a region with lowest VPC count.
5179
// Returns a string representing an IBM Cloud region name, and error.
52-
func (infoSvc *CloudInfoService) GetLeastVpcTestRegion() (string, error) {
80+
func (infoSvc *CloudInfoService) GetLeastVpcTestRegionO(options GetTestRegionOptions) (string, error) {
5381

5482
var bestregion RegionData
5583

@@ -58,7 +86,27 @@ func (infoSvc *CloudInfoService) GetLeastVpcTestRegion() (string, error) {
5886
return "", err
5987
}
6088

61-
for i, region := range regions {
89+
// if we need to filter out regions by activity tracker existence, prepare a list of those regions
90+
// NOTE: we only want to do this once at beginning and then use results below
91+
var atInstanceList []resourcecontrollerv2.ResourceInstance
92+
var atListErr error
93+
if options.ExcludeActivityTrackerRegions {
94+
atInstanceList, atListErr = infoSvc.ListResourcesByCrnServiceName("logdnaat")
95+
if atListErr != nil {
96+
log.Println("WARNING: Error retrieving Activity Tracker instances! Ignoring when selecting.")
97+
atInstanceList = []resourcecontrollerv2.ResourceInstance{}
98+
}
99+
}
100+
101+
for _, region := range regions {
102+
// if option is set, ignore region if there is existing activity tracker
103+
if options.ExcludeActivityTrackerRegions {
104+
if regionHasActivityTracker(region.Name, atInstanceList) {
105+
log.Println("Region", region.Name, "skipped due to Activity Tracker present")
106+
continue // ignore and move to next region
107+
}
108+
}
109+
62110
setErr := infoSvc.vpcService.SetServiceURL(region.Endpoint)
63111
if setErr != nil {
64112
log.Println("Failed to set a service url in vpc service")
@@ -78,8 +126,8 @@ func (infoSvc *CloudInfoService) GetLeastVpcTestRegion() (string, error) {
78126
bestregion = region
79127
log.Println("--- new best region is", bestregion.Name)
80128
break
81-
} else if i == 0 {
82-
bestregion = region // always use first region in list
129+
} else if len(bestregion.Name) == 0 {
130+
bestregion = region // always use first valid region in list
83131
log.Println("--- new best region is", bestregion.Name)
84132
} else if region.ResourceCount < bestregion.ResourceCount {
85133
bestregion = region // use if lower count
@@ -100,6 +148,25 @@ func (infoSvc *CloudInfoService) GetLeastVpcTestRegion() (string, error) {
100148
return bestregion.Name, nil
101149
}
102150

151+
// regionHasActivityTracker is a helper function to determine if a given region is represented in an array
152+
// of existing ActivityTracker resource instances.
153+
// Returns boolean true if region found
154+
func regionHasActivityTracker(region string, activityTrackerList []resourcecontrollerv2.ResourceInstance) bool {
155+
156+
// don't bother looping if empty
157+
if len(activityTrackerList) == 0 {
158+
return false
159+
}
160+
161+
for _, at := range activityTrackerList {
162+
if *at.RegionID == region {
163+
return true
164+
}
165+
}
166+
167+
return false
168+
}
169+
103170
// GetTestRegionsByPriority is a method for receiver CloudInfoService that will use the service regionsData
104171
// to determine a priorty order and region eligibility for test resources to be deployed.
105172
// The returned array will then be used by various methods to determine best region to use for different test scenarios.
@@ -216,22 +283,24 @@ func (infoSvc *CloudInfoService) GetLeastPowerConnectionZone() (string, error) {
216283

217284
for _, region := range regions {
218285

219-
connCount := countPowerConnectionsInZone(region.Name, connections)
220-
region.ResourceCount = connCount
221-
log.Println("Region", region.Name, "Resource count:", region.ResourceCount)
222-
223-
// region list is sorted by priority, so if resource count is zero then short circuit and return, it is the best region
224-
// NOTE: we will also make sure each region is not at total limit of connections, if it is we will move on to next
225-
if region.ResourceCount == 0 {
226-
bestregion = region
227-
log.Println("--- new best region is", bestregion.Name)
228-
break
229-
} else if region.ResourceCount < maxPowerConnectionsPerZone && len(bestregion.Name) == 0 {
230-
bestregion = region // always use first VALID region found in list
231-
log.Println("--- new best region is", bestregion.Name)
232-
} else if region.ResourceCount < maxPowerConnectionsPerZone && region.ResourceCount < bestregion.ResourceCount {
233-
bestregion = region // use if valid AND lower count than previous best
234-
log.Println("--- new best region is", bestregion.Name)
286+
if region.UseForTest {
287+
connCount := countPowerConnectionsInZone(region.Name, connections)
288+
region.ResourceCount = connCount
289+
log.Println("Region", region.Name, "Resource count:", region.ResourceCount)
290+
291+
// region list is sorted by priority, so if resource count is zero then short circuit and return, it is the best region
292+
// NOTE: we will also make sure each region is not at total limit of connections, if it is we will move on to next
293+
if region.ResourceCount == 0 {
294+
bestregion = region
295+
log.Println("--- new best region is", bestregion.Name)
296+
break
297+
} else if region.ResourceCount < maxPowerConnectionsPerZone && len(bestregion.Name) == 0 {
298+
bestregion = region // always use first VALID region found in list
299+
log.Println("--- new best region is", bestregion.Name)
300+
} else if region.ResourceCount < maxPowerConnectionsPerZone && region.ResourceCount < bestregion.ResourceCount {
301+
bestregion = region // use if valid AND lower count than previous best
302+
log.Println("--- new best region is", bestregion.Name)
303+
}
235304
}
236305
}
237306

@@ -243,6 +312,28 @@ func (infoSvc *CloudInfoService) GetLeastPowerConnectionZone() (string, error) {
243312
return bestregion.Name, nil
244313
}
245314

315+
// HasRegionData is a method for receiver CloudInfoService that will respond with a boolean to verify that the service instance
316+
// has region data loaded. You can use this method to determine if you need to load preference data.
317+
func (infoSvc *CloudInfoService) HasRegionData() bool {
318+
if len(infoSvc.regionsData) > 0 {
319+
return true
320+
} else {
321+
return false
322+
}
323+
}
324+
325+
// RemoveRegionForTest is a method for receiver CloudInfoService that will remove a given region for use in test considerations
326+
// by setting the UseForTest property for the region to false
327+
func (infoSvc *CloudInfoService) RemoveRegionForTest(regionID string) {
328+
// loop through region data looking for given region
329+
for i, regionData := range infoSvc.regionsData {
330+
if regionData.Name == regionID {
331+
infoSvc.regionsData[i].UseForTest = false
332+
break
333+
}
334+
}
335+
}
336+
246337
// countPowerConnectionsInZone is a private helper function that will return a count of occurances of
247338
// the provided zone in a list of existing Powercloud connections.
248339
func countPowerConnectionsInZone(zone string, connections []*PowerCloudConnectionDetail) int {
@@ -256,3 +347,10 @@ func countPowerConnectionsInZone(zone string, connections []*PowerCloudConnectio
256347

257348
return count
258349
}
350+
351+
// NewGetTestRegionOptions will return the option struct with defaults
352+
func NewGetTestRegionOptions() *GetTestRegionOptions {
353+
return &GetTestRegionOptions{
354+
ExcludeActivityTrackerRegions: false,
355+
}
356+
}

0 commit comments

Comments
 (0)