Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Guard Get<Entity>ID helpers against count/empty-list panic (#103)
The generated Get<Entity>ID and Get<Entity>ByID helpers index the result
slice at [0] as soon as Count == 1, without checking the slice is non-empty.
When the API reports count >= 1 but returns an empty list (a count/slice
mismatch seen e.g. on the metrics APIs), this panics with
'index out of range [0] with length 0' (issue #103).

Fix the generator template (generate/generate.go) to emit a length guard
before the [0] access, returning the same 'No match found' error already
used for the count == 0 case, and apply it to the generated helpers
(additive only). Adds a regression test that panics on the unpatched code
and passes with the guard.

Signed-off-by: James Peru <james@xcobean.com>
  • Loading branch information
James Peru
James Peru committed Jun 20, 2026
commit 0287d08b5e8555962e085b0fefde87fa6a8517f3
9 changes: 9 additions & 0 deletions cloudstack/AccountService.go
Original file line number Diff line number Diff line change
Expand Up @@ -1804,6 +1804,9 @@ func (s *AccountService) GetAccountID(name string, opts ...OptionFunc) (string,
}

if l.Count == 1 {
if len(l.Accounts) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
}
return l.Accounts[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -1859,6 +1862,9 @@ func (s *AccountService) GetAccountByID(id string, opts ...OptionFunc) (*Account
}

if l.Count == 1 {
if len(l.Accounts) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.Accounts[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for Account UUID: %s!", id)
Expand Down Expand Up @@ -2232,6 +2238,9 @@ func (s *AccountService) GetProjectAccountID(keyword string, projectid string, o
}

if l.Count == 1 {
if len(l.ProjectAccounts) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", keyword, l)
}
return l.ProjectAccounts[0].Id, l.Count, nil
}

Expand Down
3 changes: 3 additions & 0 deletions cloudstack/AddressService.go
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,9 @@ func (s *AddressService) GetPublicIpAddressByID(id string, opts ...OptionFunc) (
}

if l.Count == 1 {
if len(l.PublicIpAddresses) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.PublicIpAddresses[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for PublicIpAddress UUID: %s!", id)
Expand Down
6 changes: 6 additions & 0 deletions cloudstack/AffinityGroupService.go
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,9 @@ func (s *AffinityGroupService) GetAffinityGroupID(name string, opts ...OptionFun
}

if l.Count == 1 {
if len(l.AffinityGroups) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
}
return l.AffinityGroups[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -956,6 +959,9 @@ func (s *AffinityGroupService) GetAffinityGroupByID(id string, opts ...OptionFun
}

if l.Count == 1 {
if len(l.AffinityGroups) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.AffinityGroups[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for AffinityGroup UUID: %s!", id)
Expand Down
6 changes: 6 additions & 0 deletions cloudstack/AlertService.go
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,9 @@ func (s *AlertService) GetAlertID(name string, opts ...OptionFunc) (string, int,
}

if l.Count == 1 {
if len(l.Alerts) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
}
return l.Alerts[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -800,6 +803,9 @@ func (s *AlertService) GetAlertByID(id string, opts ...OptionFunc) (*Alert, int,
}

if l.Count == 1 {
if len(l.Alerts) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.Alerts[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for Alert UUID: %s!", id)
Expand Down
3 changes: 3 additions & 0 deletions cloudstack/AnnotationService.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,9 @@ func (s *AnnotationService) GetAnnotationByID(id string, opts ...OptionFunc) (*A
}

if l.Count == 1 {
if len(l.Annotations) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.Annotations[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for Annotation UUID: %s!", id)
Expand Down
24 changes: 24 additions & 0 deletions cloudstack/AutoScaleService.go
Original file line number Diff line number Diff line change
Expand Up @@ -2432,6 +2432,9 @@ func (s *AutoScaleService) GetAutoScalePolicyID(name string, opts ...OptionFunc)
}

if l.Count == 1 {
if len(l.AutoScalePolicies) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
}
return l.AutoScalePolicies[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -2487,6 +2490,9 @@ func (s *AutoScaleService) GetAutoScalePolicyByID(id string, opts ...OptionFunc)
}

if l.Count == 1 {
if len(l.AutoScalePolicies) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.AutoScalePolicies[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for AutoScalePolicy UUID: %s!", id)
Expand Down Expand Up @@ -2937,6 +2943,9 @@ func (s *AutoScaleService) GetAutoScaleVmGroupID(name string, opts ...OptionFunc
}

if l.Count == 1 {
if len(l.AutoScaleVmGroups) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
}
return l.AutoScaleVmGroups[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -2992,6 +3001,9 @@ func (s *AutoScaleService) GetAutoScaleVmGroupByID(id string, opts ...OptionFunc
}

if l.Count == 1 {
if len(l.AutoScaleVmGroups) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.AutoScaleVmGroups[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for AutoScaleVmGroup UUID: %s!", id)
Expand Down Expand Up @@ -3438,6 +3450,9 @@ func (s *AutoScaleService) GetAutoScaleVmProfileByID(id string, opts ...OptionFu
}

if l.Count == 1 {
if len(l.AutoScaleVmProfiles) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.AutoScaleVmProfiles[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for AutoScaleVmProfile UUID: %s!", id)
Expand Down Expand Up @@ -3803,6 +3818,9 @@ func (s *AutoScaleService) GetConditionByID(id string, opts ...OptionFunc) (*Con
}

if l.Count == 1 {
if len(l.Conditions) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.Conditions[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for Condition UUID: %s!", id)
Expand Down Expand Up @@ -4059,6 +4077,9 @@ func (s *AutoScaleService) GetCounterID(name string, opts ...OptionFunc) (string
}

if l.Count == 1 {
if len(l.Counters) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
}
return l.Counters[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -4114,6 +4135,9 @@ func (s *AutoScaleService) GetCounterByID(id string, opts ...OptionFunc) (*Count
}

if l.Count == 1 {
if len(l.Counters) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.Counters[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for Counter UUID: %s!", id)
Expand Down
3 changes: 3 additions & 0 deletions cloudstack/BGPPeerService.go
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,9 @@ func (s *BGPPeerService) GetBgpPeerByID(id string, opts ...OptionFunc) (*BgpPeer
}

if l.Count == 1 {
if len(l.BgpPeers) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.BgpPeers[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for BgpPeer UUID: %s!", id)
Expand Down
24 changes: 24 additions & 0 deletions cloudstack/BackupService.go
Original file line number Diff line number Diff line change
Expand Up @@ -3151,6 +3151,9 @@ func (s *BackupService) GetBackupOfferingID(keyword string, opts ...OptionFunc)
}

if l.Count == 1 {
if len(l.BackupOfferings) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", keyword, l)
}
return l.BackupOfferings[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -3206,6 +3209,9 @@ func (s *BackupService) GetBackupOfferingByID(id string, opts ...OptionFunc) (*B
}

if l.Count == 1 {
if len(l.BackupOfferings) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.BackupOfferings[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for BackupOffering UUID: %s!", id)
Expand Down Expand Up @@ -3389,6 +3395,9 @@ func (s *BackupService) GetBackupProviderOfferingID(keyword string, zoneid strin
}

if l.Count == 1 {
if len(l.BackupProviderOfferings) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", keyword, l)
}
return l.BackupProviderOfferings[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -3721,6 +3730,9 @@ func (s *BackupService) GetBackupRepositoryID(name string, opts ...OptionFunc) (
}

if l.Count == 1 {
if len(l.BackupRepositories) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
}
return l.BackupRepositories[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -3776,6 +3788,9 @@ func (s *BackupService) GetBackupRepositoryByID(id string, opts ...OptionFunc) (
}

if l.Count == 1 {
if len(l.BackupRepositories) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.BackupRepositories[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for BackupRepository UUID: %s!", id)
Expand Down Expand Up @@ -4108,6 +4123,9 @@ func (s *BackupService) GetBackupScheduleByID(id string, opts ...OptionFunc) (*B
}

if l.Count == 1 {
if len(l.BackupSchedule) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.BackupSchedule[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for BackupSchedule UUID: %s!", id)
Expand Down Expand Up @@ -4530,6 +4548,9 @@ func (s *BackupService) GetBackupID(name string, opts ...OptionFunc) (string, in
}

if l.Count == 1 {
if len(l.Backups) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
}
return l.Backups[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -4585,6 +4606,9 @@ func (s *BackupService) GetBackupByID(id string, opts ...OptionFunc) (*Backup, i
}

if l.Count == 1 {
if len(l.Backups) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.Backups[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for Backup UUID: %s!", id)
Expand Down
3 changes: 3 additions & 0 deletions cloudstack/BrocadeVCSService.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,9 @@ func (s *BrocadeVCSService) GetBrocadeVcsDeviceNetworkID(keyword string, vcsdevi
}

if l.Count == 1 {
if len(l.BrocadeVcsDeviceNetworks) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", keyword, l)
}
return l.BrocadeVcsDeviceNetworks[0].Id, l.Count, nil
}

Expand Down
3 changes: 3 additions & 0 deletions cloudstack/CertificateService.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,9 @@ func (s *CertificateService) GetTemplateDirectDownloadCertificateByID(id string,
}

if l.Count == 1 {
if len(l.TemplateDirectDownloadCertificates) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.TemplateDirectDownloadCertificates[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for TemplateDirectDownloadCertificate UUID: %s!", id)
Expand Down
15 changes: 15 additions & 0 deletions cloudstack/ClusterService.go
Original file line number Diff line number Diff line change
Expand Up @@ -1898,6 +1898,9 @@ func (s *ClusterService) GetClusterID(name string, opts ...OptionFunc) (string,
}

if l.Count == 1 {
if len(l.Clusters) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
}
return l.Clusters[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -1953,6 +1956,9 @@ func (s *ClusterService) GetClusterByID(id string, opts ...OptionFunc) (*Cluster
}

if l.Count == 1 {
if len(l.Clusters) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.Clusters[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for Cluster UUID: %s!", id)
Expand Down Expand Up @@ -2191,6 +2197,9 @@ func (s *ClusterService) GetClusterDrsPlanByID(id string, opts ...OptionFunc) (*
}

if l.Count == 1 {
if len(l.ClusterDrsPlan) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.ClusterDrsPlan[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for ClusterDrsPlan UUID: %s!", id)
Expand Down Expand Up @@ -2609,6 +2618,9 @@ func (s *ClusterService) GetClustersMetricID(name string, opts ...OptionFunc) (s
}

if l.Count == 1 {
if len(l.ClustersMetrics) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
}
return l.ClustersMetrics[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -2664,6 +2676,9 @@ func (s *ClusterService) GetClustersMetricByID(id string, opts ...OptionFunc) (*
}

if l.Count == 1 {
if len(l.ClustersMetrics) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.ClustersMetrics[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for ClustersMetric UUID: %s!", id)
Expand Down
6 changes: 6 additions & 0 deletions cloudstack/ConfigurationService.go
Original file line number Diff line number Diff line change
Expand Up @@ -1879,6 +1879,9 @@ func (s *ConfigurationService) GetCniConfigurationID(name string, opts ...Option
}

if l.Count == 1 {
if len(l.CniConfiguration) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
}
return l.CniConfiguration[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -1934,6 +1937,9 @@ func (s *ConfigurationService) GetCniConfigurationByID(id string, opts ...Option
}

if l.Count == 1 {
if len(l.CniConfiguration) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.CniConfiguration[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for CniConfiguration UUID: %s!", id)
Expand Down
6 changes: 6 additions & 0 deletions cloudstack/DiskOfferingService.go
Original file line number Diff line number Diff line change
Expand Up @@ -1445,6 +1445,9 @@ func (s *DiskOfferingService) GetDiskOfferingID(name string, opts ...OptionFunc)
}

if l.Count == 1 {
if len(l.DiskOfferings) == 0 {
return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
}
return l.DiskOfferings[0].Id, l.Count, nil
}

Expand Down Expand Up @@ -1500,6 +1503,9 @@ func (s *DiskOfferingService) GetDiskOfferingByID(id string, opts ...OptionFunc)
}

if l.Count == 1 {
if len(l.DiskOfferings) == 0 {
return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
}
return l.DiskOfferings[0], l.Count, nil
}
return nil, l.Count, fmt.Errorf("There is more then one result for DiskOffering UUID: %s!", id)
Expand Down
Loading