This is an automated email from the ASF dual-hosted git repository.

abh1sar pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 70fb49a  Scope GetTemplateByName/GetIsoByName by-ID lookup to the zone 
(#87) (#153)
70fb49a is described below

commit 70fb49a01b425d6f7c6590980ca11c9e2b83f3d8
Author: James Peru Mmbono <[email protected]>
AuthorDate: Wed Aug 5 11:09:42 2026 +0300

    Scope GetTemplateByName/GetIsoByName by-ID lookup to the zone (#87) (#153)
    
    GetTemplateByName (and GetIsoByName) resolve the id with the zoneid
    constraint via GetTemplateID, then call GetTemplateByID/GetIsoByID WITHOUT
    the zone. A template/ISO registered in multiple zones lists one row per
    zone for the same UUID, so the helper fails with 'There is more then one
    result for Template UUID: ...!' (#87).
    
    Fix the generator to pass the zone into the by-ID lookup via the existing
    WithZone option for Template/Iso, and apply it to the two generated helpers.
    WithZone is a no-op on empty zoneid and uses the id directly for a UUID.
    Adds a regression test reproducing #87.
    
    Signed-off-by: James Peru <[email protected]>
    Co-authored-by: James Peru <[email protected]>
---
 cloudstack/ISOService.go                     |  2 +-
 cloudstack/TemplateService.go                |  2 +-
 generate/generate.go                         |  8 +++-
 test/GetTemplateByNameZoneRegression_test.go | 66 ++++++++++++++++++++++++++++
 4 files changed, 75 insertions(+), 3 deletions(-)

diff --git a/cloudstack/ISOService.go b/cloudstack/ISOService.go
index 5000bc5..d98f004 100644
--- a/cloudstack/ISOService.go
+++ b/cloudstack/ISOService.go
@@ -2300,7 +2300,7 @@ func (s *ISOService) GetIsoByName(name string, isofilter 
string, zoneid string,
                return nil, count, err
        }
 
-       r, count, err := s.GetIsoByID(id, opts...)
+       r, count, err := s.GetIsoByID(id, append(opts, WithZone(zoneid))...)
        if err != nil {
                return nil, count, err
        }
diff --git a/cloudstack/TemplateService.go b/cloudstack/TemplateService.go
index e9f9071..dd7f68e 100644
--- a/cloudstack/TemplateService.go
+++ b/cloudstack/TemplateService.go
@@ -2844,7 +2844,7 @@ func (s *TemplateService) GetTemplateByName(name string, 
templatefilter string,
                return nil, count, err
        }
 
-       r, count, err := s.GetTemplateByID(id, templatefilter, opts...)
+       r, count, err := s.GetTemplateByID(id, templatefilter, append(opts, 
WithZone(zoneid))...)
        if err != nil {
                return nil, count, err
        }
diff --git a/generate/generate.go b/generate/generate.go
index 7fbeb79..c967967 100644
--- a/generate/generate.go
+++ b/generate/generate.go
@@ -1671,7 +1671,13 @@ func (s *service) generateHelperFuncs(a *API) {
                                                p("%s, ", 
s.parseParamName(ap.Name))
                                        }
                                }
-                               pn("opts...)")
+                               // Constrain the by-ID lookup to the same zone; 
otherwise a Template/ISO
+                               // registered in multiple zones returns 
multiple rows for one UUID (#87).
+                               if parseSingular(ln) == "Template" || 
parseSingular(ln) == "Iso" {
+                                       pn("append(opts, WithZone(zoneid))...)")
+                               } else {
+                                       pn("opts...)")
+                               }
                                pn("  if err != nil {")
                                pn("    return nil, count, err")
                                pn("  }")
diff --git a/test/GetTemplateByNameZoneRegression_test.go 
b/test/GetTemplateByNameZoneRegression_test.go
new file mode 100644
index 0000000..f579f42
--- /dev/null
+++ b/test/GetTemplateByNameZoneRegression_test.go
@@ -0,0 +1,66 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package test
+
+import (
+       "fmt"
+       "net/http"
+       "net/http/httptest"
+       "testing"
+
+       "github.com/apache/cloudstack-go/v2/cloudstack"
+)
+
+// Regression test for issue #87: GetTemplateByName must carry the zoneid
+// constraint into the by-ID lookup. Otherwise a template registered in 
multiple
+// zones lists multiple rows for one UUID and the helper fails with
+// "There is more then one result for Template UUID: ...!".
+func TestGetTemplateByNameScopesByIDLookupToZone(t *testing.T) {
+       const tmplID = "06145677-058a-456a-89a0-af4afd6fffcf"
+       const zoneID = "11111111-2222-3333-4444-555555555555"
+
+       server := httptest.NewServer(http.HandlerFunc(func(w 
http.ResponseWriter, r *http.Request) {
+               q := r.URL.Query()
+               w.Header().Set("Content-Type", "application/json")
+               if q.Get("id") != "" {
+                       // by-ID lookup: a multi-zone template returns one row 
PER zone unless
+                       // the zoneid is passed through (the bug). With the 
fix, zoneid is set.
+                       if q.Get("zoneid") != "" {
+                               fmt.Fprintf(w, 
`{"listtemplatesresponse":{"count":1,"template":[{"id":%q,"name":"mytmpl","zoneid":"zoneA"}]}}`,
 tmplID)
+                       } else {
+                               fmt.Fprintf(w, 
`{"listtemplatesresponse":{"count":2,"template":[{"id":%q,"name":"mytmpl","zoneid":"zoneA"},{"id":%q,"name":"mytmpl","zoneid":"zoneB"}]}}`,
 tmplID, tmplID)
+                       }
+                       return
+               }
+               // by-name lookup (GetTemplateID) resolves to a single template 
id
+               fmt.Fprintf(w, 
`{"listtemplatesresponse":{"count":1,"template":[{"id":%q,"name":"mytmpl"}]}}`, 
tmplID)
+       }))
+       defer server.Close()
+
+       client := cloudstack.NewClient(server.URL, "APIKEY", "SECRETKEY", true)
+
+       tmpl, _, err := client.Template.GetTemplateByName("mytmpl", "all", 
zoneID)
+       if err != nil {
+               t.Fatalf("expected zone-scoped GetTemplateByName to succeed, 
got error: %v", err)
+       }
+       if tmpl == nil || tmpl.Id != tmplID {
+               t.Fatalf("expected template %s, got %+v", tmplID, tmpl)
+       }
+}

Reply via email to