github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/org.go (about)

     1  /*
     2   * Copyright 2021 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
     3   */
     4  
     5  package govcd
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"net/http"
    11  	"net/url"
    12  	"strings"
    13  
    14  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    15  	"github.com/vmware/go-vcloud-director/v2/util"
    16  )
    17  
    18  type Org struct {
    19  	Org           *types.Org
    20  	client        *Client
    21  	TenantContext *TenantContext
    22  }
    23  
    24  func NewOrg(client *Client) *Org {
    25  	return &Org{
    26  		Org:    new(types.Org),
    27  		client: client,
    28  	}
    29  }
    30  
    31  // Given an org with a valid HREF, the function refetches the org
    32  // and updates the user's org data. Otherwise if the function fails,
    33  // it returns an error. Users should use refresh whenever they have
    34  // a stale org due to the creation/update/deletion of a resource
    35  // within the org or the org itself.
    36  func (org *Org) Refresh() error {
    37  	if *org == (Org{}) {
    38  		return fmt.Errorf("cannot refresh, Object is empty")
    39  	}
    40  
    41  	// Empty struct before a new unmarshal, otherwise we end up with duplicate
    42  	// elements in slices.
    43  	unmarshalledOrg := &types.Org{}
    44  
    45  	_, err := org.client.ExecuteRequest(org.Org.HREF, http.MethodGet,
    46  		"", "error refreshing organization: %s", nil, unmarshalledOrg)
    47  	if err != nil {
    48  		return err
    49  	}
    50  	org.Org = unmarshalledOrg
    51  
    52  	// The request was successful
    53  	return nil
    54  }
    55  
    56  // Given a valid catalog name, FindCatalog returns a Catalog object.
    57  // If no catalog is found, then returns an empty catalog and no error.
    58  // Otherwise it returns an error.
    59  // Deprecated: use org.GetCatalogByName instead
    60  func (org *Org) FindCatalog(catalogName string) (Catalog, error) {
    61  
    62  	for _, link := range org.Org.Link {
    63  		if link.Rel == "down" && link.Type == "application/vnd.vmware.vcloud.catalog+xml" && link.Name == catalogName {
    64  
    65  			cat := NewCatalog(org.client)
    66  
    67  			_, err := org.client.ExecuteRequest(link.HREF, http.MethodGet,
    68  				"", "error retrieving catalog: %s", nil, cat.Catalog)
    69  
    70  			return *cat, err
    71  		}
    72  	}
    73  
    74  	return Catalog{}, nil
    75  }
    76  
    77  // GetVdcByName if user specifies valid vdc name then this returns a vdc object.
    78  // If no vdc is found, then it returns an empty vdc and no error.
    79  // Otherwise it returns an empty vdc and an error.
    80  // Deprecated: use org.GetVDCByName instead
    81  func (org *Org) GetVdcByName(vdcname string) (Vdc, error) {
    82  	for _, link := range org.Org.Link {
    83  		if link.Name == vdcname {
    84  			vdc := NewVdc(org.client)
    85  			vdc.parent = org
    86  
    87  			_, err := org.client.ExecuteRequest(link.HREF, http.MethodGet,
    88  				"", "error retrieving vdc: %s", nil, vdc.Vdc)
    89  
    90  			return *vdc, err
    91  		}
    92  	}
    93  	return Vdc{}, nil
    94  }
    95  
    96  // CreateCatalog creates a catalog with specified name and description
    97  func CreateCatalog(client *Client, links types.LinkList, Name, Description string) (AdminCatalog, error) {
    98  	adminCatalog, err := CreateCatalogWithStorageProfile(client, links, Name, Description, nil)
    99  	if err != nil {
   100  		return AdminCatalog{}, nil
   101  	}
   102  	return *adminCatalog, nil
   103  }
   104  
   105  // CreateCatalogWithStorageProfile is like CreateCatalog, but allows to specify storage profile
   106  func CreateCatalogWithStorageProfile(client *Client, links types.LinkList, Name, Description string, storageProfiles *types.CatalogStorageProfiles) (*AdminCatalog, error) {
   107  	reqCatalog := &types.Catalog{
   108  		Name:        Name,
   109  		Description: Description,
   110  	}
   111  	vcomp := &types.AdminCatalog{
   112  		Xmlns:                  types.XMLNamespaceVCloud,
   113  		Catalog:                *reqCatalog,
   114  		CatalogStorageProfiles: storageProfiles,
   115  	}
   116  
   117  	var createOrgLink *types.Link
   118  	for _, link := range links {
   119  		if link.Rel == "add" && link.Type == types.MimeAdminCatalog {
   120  			util.Logger.Printf("[TRACE] Create org - found the proper link for request, HREF: %s, "+
   121  				"name: %s, type: %s, id: %s, rel: %s \n", link.HREF, link.Name, link.Type, link.ID, link.Rel)
   122  			createOrgLink = link
   123  		}
   124  	}
   125  
   126  	if createOrgLink == nil {
   127  		return nil, fmt.Errorf("creating catalog failed to find url")
   128  	}
   129  
   130  	catalog := NewAdminCatalog(client)
   131  	_, err := client.ExecuteRequest(createOrgLink.HREF, http.MethodPost,
   132  		"application/vnd.vmware.admin.catalog+xml", "error creating catalog: %s", vcomp, catalog.AdminCatalog)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	err = catalog.WaitForTasks()
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	return catalog, nil
   142  }
   143  
   144  // CreateCatalog creates a catalog with given name and description under
   145  // the given organization. Returns an Catalog that contains a creation
   146  // task.
   147  // API Documentation: https://code.vmware.com/apis/220/vcloud#/doc/doc/operations/POST-CreateCatalog.html
   148  func (org *Org) CreateCatalog(name, description string) (Catalog, error) {
   149  	catalog, err := org.CreateCatalogWithStorageProfile(name, description, nil)
   150  	if err != nil {
   151  		return Catalog{}, err
   152  	}
   153  	catalog.parent = org
   154  
   155  	err = catalog.Refresh()
   156  	if err != nil {
   157  		return Catalog{}, err
   158  	}
   159  	// Make sure that the creation task is finished
   160  	err = catalog.WaitForTasks()
   161  	if err != nil {
   162  		return Catalog{}, err
   163  	}
   164  	return *catalog, nil
   165  }
   166  
   167  // CreateCatalogWithStorageProfile is like CreateCatalog but additionally allows to specify storage profiles
   168  func (org *Org) CreateCatalogWithStorageProfile(name, description string, storageProfiles *types.CatalogStorageProfiles) (*Catalog, error) {
   169  	catalog := NewCatalog(org.client)
   170  	adminCatalog, err := CreateCatalogWithStorageProfile(org.client, org.Org.Link, name, description, storageProfiles)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  	catalog.Catalog = &adminCatalog.AdminCatalog.Catalog
   175  	catalog.parent = org
   176  	return catalog, nil
   177  }
   178  
   179  func validateVdcConfiguration(vdcDefinition *types.VdcConfiguration) error {
   180  	if vdcDefinition.Name == "" {
   181  		return errors.New("VdcConfiguration missing required field: Name")
   182  	}
   183  	if vdcDefinition.AllocationModel == "" {
   184  		return errors.New("VdcConfiguration missing required field: AllocationModel")
   185  	}
   186  	if vdcDefinition.ComputeCapacity == nil {
   187  		return errors.New("VdcConfiguration missing required field: ComputeCapacity")
   188  	}
   189  	if len(vdcDefinition.ComputeCapacity) != 1 {
   190  		return errors.New("VdcConfiguration invalid field: ComputeCapacity must only have one element")
   191  	}
   192  	if vdcDefinition.ComputeCapacity[0] == nil {
   193  		return errors.New("VdcConfiguration missing required field: ComputeCapacity[0]")
   194  	}
   195  	if vdcDefinition.ComputeCapacity[0].CPU == nil {
   196  		return errors.New("VdcConfiguration missing required field: ComputeCapacity[0].CPU")
   197  	}
   198  	if vdcDefinition.ComputeCapacity[0].CPU.Units == "" {
   199  		return errors.New("VdcConfiguration missing required field: ComputeCapacity[0].CPU.Units")
   200  	}
   201  	if vdcDefinition.ComputeCapacity[0].Memory == nil {
   202  		return errors.New("VdcConfiguration missing required field: ComputeCapacity[0].Memory")
   203  	}
   204  	if vdcDefinition.ComputeCapacity[0].Memory.Units == "" {
   205  		return errors.New("VdcConfiguration missing required field: ComputeCapacity[0].Memory.Units")
   206  	}
   207  	if vdcDefinition.VdcStorageProfile == nil || len(vdcDefinition.VdcStorageProfile) == 0 {
   208  		return errors.New("VdcConfiguration missing required field: VdcStorageProfile")
   209  	}
   210  	if vdcDefinition.VdcStorageProfile[0].Units == "" {
   211  		return errors.New("VdcConfiguration missing required field: VdcStorageProfile.Units")
   212  	}
   213  	if vdcDefinition.ProviderVdcReference == nil {
   214  		return errors.New("VdcConfiguration missing required field: ProviderVdcReference")
   215  	}
   216  	if vdcDefinition.ProviderVdcReference.HREF == "" {
   217  		return errors.New("VdcConfiguration missing required field: ProviderVdcReference.HREF")
   218  	}
   219  	return nil
   220  }
   221  
   222  // GetCatalogByHref  finds a Catalog by HREF
   223  // On success, returns a pointer to the Catalog structure and a nil error
   224  // On failure, returns a nil pointer and an error
   225  func (org *Org) GetCatalogByHref(catalogHref string) (*Catalog, error) {
   226  	cat := NewCatalog(org.client)
   227  
   228  	_, err := org.client.ExecuteRequest(catalogHref, http.MethodGet,
   229  		"", "error retrieving catalog: %s", nil, cat.Catalog)
   230  	if err != nil {
   231  		return nil, err
   232  	}
   233  	// The request was successful
   234  	cat.parent = org
   235  	return cat, nil
   236  }
   237  
   238  // GetCatalogByName  finds a Catalog by Name
   239  // On success, returns a pointer to the Catalog structure and a nil error
   240  // On failure, returns a nil pointer and an error
   241  //
   242  // refresh has no effect here, but is kept to preserve signature
   243  func (org *Org) GetCatalogByName(catalogName string, refresh bool) (*Catalog, error) {
   244  	vdcQuery, err := org.queryCatalogByName(catalogName)
   245  	if ContainsNotFound(err) {
   246  		return nil, ErrorEntityNotFound
   247  	}
   248  	if err != nil {
   249  		return nil, fmt.Errorf("error querying Catalog: %s", err)
   250  	}
   251  	// This is not an AdminOrg and admin HREF must be removed if it exists
   252  	href := strings.Replace(vdcQuery.HREF, "/api/admin", "/api", 1)
   253  	return org.GetCatalogByHref(href)
   254  }
   255  
   256  // GetCatalogById finds a Catalog by ID
   257  // On success, returns a pointer to the Catalog structure and a nil error
   258  // On failure, returns a nil pointer and an error
   259  func (org *Org) GetCatalogById(catalogId string, refresh bool) (*Catalog, error) {
   260  	vdcQuery, err := org.queryCatalogById(catalogId)
   261  	if ContainsNotFound(err) {
   262  		return nil, ErrorEntityNotFound
   263  	}
   264  	if err != nil {
   265  		return nil, fmt.Errorf("error querying Catalog: %s", err)
   266  	}
   267  
   268  	// This is not an AdminOrg and admin HREF must be removed if it exists
   269  	href := strings.Replace(vdcQuery.HREF, "/api/admin", "/api", 1)
   270  	return org.GetCatalogByHref(href)
   271  }
   272  
   273  // GetCatalogByNameOrId finds a Catalog by name or ID
   274  // On success, returns a pointer to the Catalog structure and a nil error
   275  // On failure, returns a nil pointer and an error
   276  func (org *Org) GetCatalogByNameOrId(identifier string, refresh bool) (*Catalog, error) {
   277  	getByName := func(name string, refresh bool) (interface{}, error) {
   278  		return org.GetCatalogByName(name, refresh)
   279  	}
   280  	getById := func(id string, refresh bool) (interface{}, error) {
   281  		return org.GetCatalogById(id, refresh)
   282  	}
   283  	entity, err := getEntityByNameOrIdSkipNonId(getByName, getById, identifier, refresh)
   284  	if entity == nil {
   285  		return nil, err
   286  	}
   287  	return entity.(*Catalog), err
   288  }
   289  
   290  // GetVDCByHref finds a VDC by HREF
   291  // On success, returns a pointer to the VDC structure and a nil error
   292  // On failure, returns a nil pointer and an error
   293  func (org *Org) GetVDCByHref(vdcHref string) (*Vdc, error) {
   294  	vdc := NewVdc(org.client)
   295  	_, err := org.client.ExecuteRequest(vdcHref, http.MethodGet,
   296  		"", "error retrieving VDC: %s", nil, vdc.Vdc)
   297  	if err != nil {
   298  		return nil, err
   299  	}
   300  	// The request was successful
   301  	vdc.parent = org
   302  	return vdc, nil
   303  }
   304  
   305  // GetVDCByName finds a VDC by Name
   306  // On success, returns a pointer to the VDC structure and a nil error
   307  // On failure, returns a nil pointer and an error
   308  //
   309  // refresh has no effect and is kept to preserve signature
   310  func (org *Org) GetVDCByName(vdcName string, refresh bool) (*Vdc, error) {
   311  	vdcQuery, err := org.queryOrgVdcByName(vdcName)
   312  	if ContainsNotFound(err) {
   313  		return nil, ErrorEntityNotFound
   314  	}
   315  	if err != nil {
   316  		return nil, fmt.Errorf("error querying VDC: %s", err)
   317  	}
   318  	// This is not an AdminOrg and admin HREF must be removed if it exists
   319  	href := strings.Replace(vdcQuery.HREF, "/api/admin", "/api", 1)
   320  	return org.GetVDCByHref(href)
   321  }
   322  
   323  // GetVDCById finds a VDC by ID
   324  // On success, returns a pointer to the VDC structure and a nil error
   325  // On failure, returns a nil pointer and an error
   326  //
   327  // refresh has no effect and is kept to preserve signature
   328  func (org *Org) GetVDCById(vdcId string, refresh bool) (*Vdc, error) {
   329  	vdcQuery, err := org.queryOrgVdcById(vdcId)
   330  	if ContainsNotFound(err) {
   331  		return nil, ErrorEntityNotFound
   332  	}
   333  	if err != nil {
   334  		return nil, fmt.Errorf("error querying VDC: %s", err)
   335  	}
   336  
   337  	// This is not an AdminOrg and admin HREF must be removed if it exists
   338  	href := strings.Replace(vdcQuery.HREF, "/api/admin", "/api", 1)
   339  	return org.GetVDCByHref(href)
   340  }
   341  
   342  // GetVDCByNameOrId finds a VDC by name or ID
   343  // On success, returns a pointer to the VDC structure and a nil error
   344  // On failure, returns a nil pointer and an error
   345  //
   346  // refresh has no effect and is kept to preserve signature
   347  func (org *Org) GetVDCByNameOrId(identifier string, refresh bool) (*Vdc, error) {
   348  	getByName := func(name string, refresh bool) (interface{}, error) {
   349  		return org.GetVDCByName(name, refresh)
   350  	}
   351  	getById := func(id string, refresh bool) (interface{}, error) {
   352  		return org.GetVDCById(id, refresh)
   353  	}
   354  	entity, err := getEntityByNameOrIdSkipNonId(getByName, getById, identifier, refresh)
   355  	if entity == nil {
   356  		return nil, err
   357  	}
   358  	return entity.(*Vdc), err
   359  }
   360  
   361  // QueryCatalogList returns a list of catalogs for this organization
   362  func (org *Org) QueryCatalogList() ([]*types.CatalogRecord, error) {
   363  	util.Logger.Printf("[DEBUG] QueryCatalogList with Org HREF %s", org.Org.HREF)
   364  	filter := map[string]string{
   365  		"org": org.Org.HREF,
   366  	}
   367  	return queryCatalogList(org.client, filter)
   368  }
   369  
   370  // GetTaskList returns Tasks for Organization and error.
   371  func (org *Org) GetTaskList() (*types.TasksList, error) {
   372  
   373  	for _, link := range org.Org.Link {
   374  		if link.Rel == "down" && link.Type == "application/vnd.vmware.vcloud.tasksList+xml" {
   375  
   376  			tasksList := &types.TasksList{}
   377  
   378  			_, err := org.client.ExecuteRequest(link.HREF, http.MethodGet, "",
   379  				"error getting taskList: %s", nil, tasksList)
   380  			if err != nil {
   381  				return nil, err
   382  			}
   383  
   384  			return tasksList, nil
   385  		}
   386  	}
   387  
   388  	return nil, fmt.Errorf("link not found")
   389  }
   390  
   391  // queryOrgVdcByName returns a single QueryResultOrgVdcRecordType
   392  func (org *Org) queryOrgVdcByName(vdcName string) (*types.QueryResultOrgVdcRecordType, error) {
   393  	filterFields := map[string]string{
   394  		"org":     org.Org.HREF,
   395  		"orgName": org.Org.Name,
   396  		"name":    vdcName,
   397  	}
   398  	allVdcs, err := queryOrgVdcList(org.client, filterFields)
   399  	if err != nil {
   400  		return nil, err
   401  	}
   402  
   403  	if allVdcs == nil || len(allVdcs) < 1 {
   404  		return nil, ErrorEntityNotFound
   405  	}
   406  
   407  	if len(allVdcs) > 1 {
   408  		return nil, fmt.Errorf("found more than 1 VDC with Name '%s'", vdcName)
   409  	}
   410  
   411  	return allVdcs[0], nil
   412  }
   413  
   414  // queryOrgVdcById returns a single QueryResultOrgVdcRecordType
   415  func (org *Org) queryOrgVdcById(vdcId string) (*types.QueryResultOrgVdcRecordType, error) {
   416  	filterMap := map[string]string{
   417  		"org":     org.Org.HREF,
   418  		"orgName": org.Org.Name,
   419  		"id":      vdcId,
   420  	}
   421  	allVdcs, err := queryOrgVdcList(org.client, filterMap)
   422  
   423  	if err != nil {
   424  		return nil, err
   425  	}
   426  
   427  	if len(allVdcs) < 1 {
   428  		return nil, ErrorEntityNotFound
   429  	}
   430  
   431  	return allVdcs[0], nil
   432  }
   433  
   434  // queryCatalogByName returns a single CatalogRecord
   435  func (org *Org) queryCatalogByName(catalogName string) (*types.CatalogRecord, error) {
   436  	filterMap := map[string]string{
   437  		// Not injecting `org` or `orgName` here because shared catalogs may also appear here and they would have different
   438  		// parent Org
   439  		// "org":     org.Org.HREF,
   440  		// "orgName": org.Org.Name,
   441  		"name": catalogName,
   442  	}
   443  	allCatalogs, err := queryCatalogList(org.client, filterMap)
   444  	if err != nil {
   445  		return nil, err
   446  	}
   447  
   448  	if allCatalogs == nil || len(allCatalogs) < 1 {
   449  		return nil, ErrorEntityNotFound
   450  	}
   451  
   452  	// To conform with this API standard it would be best to return an error if more than 1 item is found, but because
   453  	// previous method of getting Catalog by Name returned the first result we are doing the same here
   454  	// if len(allCatalogs) > 1 {
   455  	// 	return nil, fmt.Errorf("found more than 1 Catalog with Name '%s'", catalogName)
   456  	// }
   457  
   458  	var localCatalog *types.CatalogRecord
   459  	// if multiple results are found - return the one defined in `org` (local)
   460  	if len(allCatalogs) > 1 {
   461  		util.Logger.Printf("[DEBUG] org.queryCatalogByName found %d Catalogs by name '%s'", len(allCatalogs), catalogName)
   462  		for _, catalog := range allCatalogs {
   463  			util.Logger.Printf("[DEBUG] org.queryCatalogByName found a Catalog by name '%s' in Org '%s'", catalogName, catalog.OrgName)
   464  			if catalog.OrgName == org.Org.Name {
   465  				util.Logger.Printf("[DEBUG] org.queryCatalogByName Catalog '%s' is local for Org '%s'. Prioritising it",
   466  					catalogName, org.Org.Name)
   467  				// Not interrupting the loop here to still dump all results to logs
   468  				localCatalog = catalog
   469  			}
   470  		}
   471  	}
   472  
   473  	// local catalog was found - return it
   474  	if localCatalog != nil {
   475  		return localCatalog, nil
   476  	}
   477  
   478  	// If only one catalog is found or multiple catalogs with no local ones - return the first one
   479  	return allCatalogs[0], nil
   480  }
   481  
   482  // queryCatalogById returns a single QueryResultOrgVdcRecordType
   483  func (org *Org) queryCatalogById(catalogId string) (*types.CatalogRecord, error) {
   484  	filterMap := map[string]string{
   485  		// Not injecting `org` or `orgName` here because shared catalogs may also appear here and they would have different
   486  		// parent Org
   487  		// "org":     org.Org.HREF,
   488  		// "orgName": org.Org.Name,
   489  		"id": catalogId,
   490  	}
   491  	allCatalogs, err := queryCatalogList(org.client, filterMap)
   492  
   493  	if err != nil {
   494  		return nil, err
   495  	}
   496  
   497  	if len(allCatalogs) < 1 {
   498  		return nil, ErrorEntityNotFound
   499  	}
   500  
   501  	return allCatalogs[0], nil
   502  }
   503  
   504  // QueryOrgVdcList returns all Org VDCs using query endpoint
   505  //
   506  // Note. Being a 'System' user it will not return any VDC
   507  func (org *Org) QueryOrgVdcList() ([]*types.QueryResultOrgVdcRecordType, error) {
   508  	filter := map[string]string{
   509  		"org": org.Org.HREF,
   510  	}
   511  
   512  	return queryOrgVdcList(org.client, filter)
   513  }
   514  
   515  // queryOrgVdcList performs an `orgVdc` or `adminOrgVdc` (for System user) and optionally applies filterFields
   516  func queryOrgVdcList(client *Client, filterFields map[string]string) ([]*types.QueryResultOrgVdcRecordType, error) {
   517  	util.Logger.Printf("[DEBUG] queryOrgVdcList with filter %#v", filterFields)
   518  	queryType := client.GetQueryType(types.QtOrgVdc)
   519  
   520  	filter := map[string]string{
   521  		"type": queryType,
   522  	}
   523  
   524  	// When a map of filters with non empty keys and values is supplied - apply it
   525  	if filterFields != nil {
   526  		filterSlice := make([]string, 0)
   527  
   528  		for filterFieldName, filterFieldValue := range filterFields {
   529  			// Do not inject 'org' filter for System user as API returns an error
   530  			if !client.IsSysAdmin && filterFieldName == "org" {
   531  				continue
   532  			}
   533  
   534  			if filterFieldName != "" && filterFieldValue != "" {
   535  				filterText := fmt.Sprintf("%s==%s", filterFieldName, url.QueryEscape(filterFieldValue))
   536  				filterSlice = append(filterSlice, filterText)
   537  			}
   538  		}
   539  
   540  		if len(filterSlice) > 0 {
   541  			filter["filter"] = strings.Join(filterSlice, ";")
   542  			filter["filterEncoded"] = "true"
   543  		}
   544  	}
   545  
   546  	results, err := client.cumulativeQuery(queryType, nil, filter)
   547  	if err != nil {
   548  		return nil, fmt.Errorf("error querying Org VDCs %s", err)
   549  	}
   550  
   551  	if client.IsSysAdmin {
   552  		return results.Results.OrgVdcAdminRecord, nil
   553  	} else {
   554  		return results.Results.OrgVdcRecord, nil
   555  	}
   556  }
   557  
   558  func queryCatalogList(client *Client, filterFields map[string]string) ([]*types.CatalogRecord, error) {
   559  	util.Logger.Printf("[DEBUG] queryCatalogList with filter %#v", filterFields)
   560  	queryType := client.GetQueryType(types.QtCatalog)
   561  
   562  	filter := map[string]string{
   563  		"type": queryType,
   564  	}
   565  
   566  	// When a map of filters with non empty keys and values is supplied - apply it
   567  	if filterFields != nil {
   568  		filterSlice := make([]string, 0)
   569  
   570  		for filterFieldName, filterFieldValue := range filterFields {
   571  			// Do not inject 'org' filter for System user as API returns an error
   572  			if !client.IsSysAdmin && filterFieldName == "org" {
   573  				continue
   574  			}
   575  
   576  			if filterFieldName != "" && filterFieldValue != "" {
   577  				filterText := fmt.Sprintf("%s==%s", filterFieldName, url.QueryEscape(filterFieldValue))
   578  				filterSlice = append(filterSlice, filterText)
   579  			}
   580  		}
   581  
   582  		if len(filterSlice) > 0 {
   583  			filter["filter"] = strings.Join(filterSlice, ";")
   584  			filter["filterEncoded"] = "true"
   585  		}
   586  	}
   587  
   588  	results, err := client.cumulativeQuery(queryType, nil, filter)
   589  	if err != nil {
   590  		return nil, err
   591  	}
   592  
   593  	var catalogs []*types.CatalogRecord
   594  
   595  	if client.IsSysAdmin {
   596  		catalogs = results.Results.AdminCatalogRecord
   597  	} else {
   598  		catalogs = results.Results.CatalogRecord
   599  	}
   600  	util.Logger.Printf("[DEBUG] QueryCatalogList returned with : %#v and error: %s", catalogs, err)
   601  	return catalogs, nil
   602  }
   603  
   604  // GetVappByHref returns a vApp reference by running a VCD API call
   605  // If no valid vApp is found, it returns a nil VApp reference and an error
   606  func (org *Org) GetVAppByHref(vappHref string) (*VApp, error) {
   607  	newVapp := NewVApp(org.client)
   608  
   609  	_, err := org.client.ExecuteRequest(vappHref, http.MethodGet,
   610  		"", "error retrieving vApp: %s", nil, newVapp.VApp)
   611  
   612  	if err != nil {
   613  		return nil, err
   614  	}
   615  	return newVapp, nil
   616  }