github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/admincatalog.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 "fmt" 9 "net/http" 10 "net/url" 11 "strings" 12 "time" 13 14 "github.com/vmware/go-vcloud-director/v2/types/v56" 15 "github.com/vmware/go-vcloud-director/v2/util" 16 ) 17 18 // AdminCatalog is a admin view of a VMware Cloud Director Catalog 19 // To be able to get an AdminCatalog representation, users must have 20 // admin credentials to the System org. AdminCatalog is used 21 // for creating, updating, and deleting a Catalog. 22 // Definition: https://code.vmware.com/apis/220/vcloud#/doc/doc/types/AdminCatalogType.html 23 type AdminCatalog struct { 24 AdminCatalog *types.AdminCatalog 25 client *Client 26 parent organization 27 } 28 29 func NewAdminCatalog(client *Client) *AdminCatalog { 30 return &AdminCatalog{ 31 AdminCatalog: new(types.AdminCatalog), 32 client: client, 33 } 34 } 35 36 func NewAdminCatalogWithParent(client *Client, parent organization) *AdminCatalog { 37 return &AdminCatalog{ 38 AdminCatalog: new(types.AdminCatalog), 39 client: client, 40 parent: parent, 41 } 42 } 43 44 // Delete deletes the Catalog, returning an error if the vCD call fails. 45 // Link to API call: https://code.vmware.com/apis/220/vcloud#/doc/doc/operations/DELETE-Catalog.html 46 func (adminCatalog *AdminCatalog) Delete(force, recursive bool) error { 47 catalog := NewCatalog(adminCatalog.client) 48 catalog.Catalog = &adminCatalog.AdminCatalog.Catalog 49 return catalog.Delete(force, recursive) 50 } 51 52 // Update updates the Catalog definition from current Catalog struct contents. 53 // Any differences that may be legally applied will be updated. 54 // Returns an error if the call to vCD fails. Update automatically performs 55 // a refresh with the admin catalog it gets back from the rest api 56 // Link to API call: https://code.vmware.com/apis/220/vcloud#/doc/doc/operations/PUT-Catalog.html 57 func (adminCatalog *AdminCatalog) Update() error { 58 reqCatalog := &types.Catalog{ 59 Name: adminCatalog.AdminCatalog.Catalog.Name, 60 Description: adminCatalog.AdminCatalog.Description, 61 } 62 vcomp := &types.AdminCatalog{ 63 Xmlns: types.XMLNamespaceVCloud, 64 Catalog: *reqCatalog, 65 CatalogStorageProfiles: adminCatalog.AdminCatalog.CatalogStorageProfiles, 66 IsPublished: adminCatalog.AdminCatalog.IsPublished, 67 } 68 catalog := &types.AdminCatalog{} 69 _, err := adminCatalog.client.ExecuteRequest(adminCatalog.AdminCatalog.HREF, http.MethodPut, 70 "application/vnd.vmware.admin.catalog+xml", "error updating catalog: %s", vcomp, catalog) 71 adminCatalog.AdminCatalog = catalog 72 return err 73 } 74 75 // UploadOvf uploads an ova file to a catalog. This method only uploads bits to vCD spool area. 76 // Returns errors if any occur during upload from vCD or upload process. On upload fail client may need to 77 // remove vCD catalog item which waits for files to be uploaded. Files from ova are extracted to system 78 // temp folder "govcd+random number" and left for inspection on error. 79 func (adminCatalog *AdminCatalog) UploadOvf(ovaFileName, itemName, description string, uploadPieceSize int64) (UploadTask, error) { 80 catalog := NewCatalog(adminCatalog.client) 81 catalog.Catalog = &adminCatalog.AdminCatalog.Catalog 82 catalog.parent = adminCatalog.parent 83 return catalog.UploadOvf(ovaFileName, itemName, description, uploadPieceSize) 84 } 85 86 // Refresh fetches a fresh copy of the Admin Catalog 87 func (adminCatalog *AdminCatalog) Refresh() error { 88 if *adminCatalog == (AdminCatalog{}) || adminCatalog.AdminCatalog.HREF == "" { 89 return fmt.Errorf("cannot refresh, Object is empty or HREF is empty") 90 } 91 92 refreshedCatalog := &types.AdminCatalog{} 93 94 _, err := adminCatalog.client.ExecuteRequest(adminCatalog.AdminCatalog.HREF, http.MethodGet, 95 "", "error refreshing VDC: %s", nil, refreshedCatalog) 96 if err != nil { 97 return err 98 } 99 adminCatalog.AdminCatalog = refreshedCatalog 100 101 return nil 102 } 103 104 // getOrgInfo finds the organization to which the admin catalog belongs, and returns its name and ID 105 func (adminCatalog *AdminCatalog) getOrgInfo() (*TenantContext, error) { 106 return adminCatalog.getTenantContext() 107 } 108 109 // PublishToExternalOrganizations publishes a catalog to external organizations. 110 func (cat *AdminCatalog) PublishToExternalOrganizations(publishExternalCatalog types.PublishExternalCatalogParams) error { 111 if cat.AdminCatalog == nil { 112 return fmt.Errorf("cannot publish to external organization, Object is empty") 113 } 114 115 url := cat.AdminCatalog.HREF 116 if url == "nil" || url == "" { 117 return fmt.Errorf("cannot publish to external organization, HREF is empty") 118 } 119 120 tenantContext, err := cat.getTenantContext() 121 if err != nil { 122 return fmt.Errorf("cannot publish to external organization, tenant context error: %s", err) 123 } 124 125 err = publishToExternalOrganizations(cat.client, url, tenantContext, publishExternalCatalog) 126 if err != nil { 127 return err 128 } 129 130 err = cat.Refresh() 131 if err != nil { 132 return err 133 } 134 135 return err 136 } 137 138 // CreateCatalogFromSubscriptionAsync creates a new catalog by subscribing to a published catalog 139 // Parameter subscription needs to be filled manually 140 func (org *AdminOrg) CreateCatalogFromSubscriptionAsync(subscription types.ExternalCatalogSubscription, 141 storageProfiles *types.CatalogStorageProfiles, 142 catalogName, password string, localCopy bool) (*AdminCatalog, error) { 143 144 // If the receiving Org doesn't have any VDCs, it means that there is no storage that can be used 145 // by a catalog 146 if len(org.AdminOrg.Vdcs.Vdcs) == 0 { 147 return nil, fmt.Errorf("org %s does not have any storage to support a catalog", org.AdminOrg.Name) 148 } 149 href := "" 150 151 // The subscribed catalog creation is like a regular catalog creation, with the 152 // difference that the subscription details are filled in 153 for _, link := range org.AdminOrg.Link { 154 if link.Rel == "add" && link.Type == types.MimeAdminCatalog { 155 href = link.HREF 156 break 157 } 158 } 159 if href == "" { 160 return nil, fmt.Errorf("catalog creation link not found for org %s", org.AdminOrg.Name) 161 } 162 adminCatalog := NewAdminCatalog(org.client) 163 reqCatalog := &types.Catalog{ 164 Name: catalogName, 165 } 166 adminCatalog.AdminCatalog = &types.AdminCatalog{ 167 Xmlns: types.XMLNamespaceVCloud, 168 Catalog: *reqCatalog, 169 CatalogStorageProfiles: storageProfiles, 170 ExternalCatalogSubscription: &types.ExternalCatalogSubscription{ 171 LocalCopy: localCopy, 172 Password: password, 173 Location: subscription.Location, 174 SubscribeToExternalFeeds: true, 175 }, 176 } 177 178 adminCatalog.AdminCatalog.ExternalCatalogSubscription.Password = password 179 adminCatalog.AdminCatalog.ExternalCatalogSubscription.LocalCopy = localCopy 180 _, err := org.client.ExecuteRequest(href, http.MethodPost, types.MimeAdminCatalog, 181 "error subscribing to catalog: %s", adminCatalog.AdminCatalog, adminCatalog.AdminCatalog) 182 if err != nil { 183 return nil, err 184 } 185 // Before returning, check that there are no failing tasks 186 err = adminCatalog.Refresh() 187 if err != nil { 188 return nil, fmt.Errorf("error refreshing subscribed catalog %s: %s", catalogName, err) 189 } 190 if adminCatalog.AdminCatalog.Tasks != nil { 191 msg := "" 192 for _, task := range adminCatalog.AdminCatalog.Tasks.Task { 193 if task.Status == "error" { 194 if task.Error != nil { 195 msg = task.Error.Error() 196 } 197 return nil, fmt.Errorf("error while subscribing catalog %s (task %s): %s", catalogName, task.Name, msg) 198 } 199 if task.Tasks != nil { 200 for _, subTask := range task.Tasks.Task { 201 if subTask.Status == "error" { 202 if subTask.Error != nil { 203 msg = subTask.Error.Error() 204 } 205 return nil, fmt.Errorf("error while subscribing catalog %s (subTask %s): %s", catalogName, subTask.Name, msg) 206 } 207 208 } 209 } 210 } 211 } 212 return adminCatalog, nil 213 } 214 215 // FullSubscriptionUrl returns the subscription URL from a publishing catalog 216 // adding the HOST if needed 217 func (cat *AdminCatalog) FullSubscriptionUrl() (string, error) { 218 err := cat.Refresh() 219 if err != nil { 220 return "", err 221 } 222 if cat.AdminCatalog.PublishExternalCatalogParams == nil { 223 return "", fmt.Errorf("AdminCatalog %s has no publishing parameters", cat.AdminCatalog.Name) 224 } 225 subscriptionUrl, err := buildFullUrl(cat.AdminCatalog.PublishExternalCatalogParams.CatalogPublishedUrl, cat.AdminCatalog.HREF) 226 if err != nil { 227 return "", err 228 } 229 return subscriptionUrl, nil 230 } 231 232 // buildFullUrl gets a (possibly incomplete) URL and returns it completed, using the provided HREF as basis 233 func buildFullUrl(subscriptionUrl, href string) (string, error) { 234 var err error 235 if !IsValidUrl(subscriptionUrl) { 236 // Get the entity base URL 237 cutPosition := strings.Index(href, "/api") 238 host := href[:cutPosition] 239 subscriptionUrl, err = url.JoinPath(host, subscriptionUrl) 240 if err != nil { 241 return "", err 242 } 243 } 244 return subscriptionUrl, nil 245 } 246 247 // IsValidUrl returns true if the given URL is complete and usable 248 func IsValidUrl(str string) bool { 249 u, err := url.Parse(str) 250 return err == nil && u.Scheme != "" && u.Host != "" 251 } 252 253 // CreateCatalogFromSubscription is a wrapper around CreateCatalogFromSubscriptionAsync 254 // After catalog creation, it waits for the import tasks to complete within a given timeout 255 func (org *AdminOrg) CreateCatalogFromSubscription(subscription types.ExternalCatalogSubscription, 256 storageProfiles *types.CatalogStorageProfiles, 257 catalogName, password string, localCopy bool, timeout time.Duration) (*AdminCatalog, error) { 258 noTimeout := timeout == 0 259 adminCatalog, err := org.CreateCatalogFromSubscriptionAsync(subscription, storageProfiles, catalogName, password, localCopy) 260 if err != nil { 261 return nil, err 262 } 263 start := time.Now() 264 for noTimeout || time.Since(start) < timeout { 265 if noTimeout { 266 util.Logger.Printf("[TRACE] [CreateCatalogFromSubscription] no timeout given - Elapsed %s", time.Since(start)) 267 } 268 err = adminCatalog.Refresh() 269 if err != nil { 270 return nil, err 271 } 272 if ResourceComplete(adminCatalog.AdminCatalog.Tasks) { 273 return adminCatalog, nil 274 } 275 } 276 return nil, fmt.Errorf("adminCatalog %s still not complete after %s", adminCatalog.AdminCatalog.Name, timeout) 277 } 278 279 // WaitForTasks waits for the catalog's tasks to complete 280 func (cat *AdminCatalog) WaitForTasks() error { 281 if ResourceInProgress(cat.AdminCatalog.Tasks) { 282 err := WaitResource(func() (*types.TasksInProgress, error) { 283 err := cat.Refresh() 284 if err != nil { 285 return nil, err 286 } 287 return cat.AdminCatalog.Tasks, nil 288 }) 289 return err 290 } 291 return nil 292 } 293 294 // Sync synchronises a subscribed AdminCatalog 295 func (cat *AdminCatalog) Sync() error { 296 // if the catalog was not subscribed, return 297 if cat.AdminCatalog.ExternalCatalogSubscription == nil || cat.AdminCatalog.ExternalCatalogSubscription.Location == "" { 298 return nil 299 } 300 // The sync operation is only available for Catalog, not AdminCatalog. 301 // We use the embedded Catalog object for this purpose 302 catalogHref, err := cat.GetCatalogHref() 303 if err != nil || catalogHref == "" { 304 return fmt.Errorf("empty catalog HREF for admin catalog %s", cat.AdminCatalog.Name) 305 } 306 err = cat.WaitForTasks() 307 if err != nil { 308 return err 309 } 310 return elementSync(cat.client, catalogHref, "admin catalog") 311 } 312 313 // LaunchSync starts synchronisation of a subscribed AdminCatalog 314 func (cat *AdminCatalog) LaunchSync() (*Task, error) { 315 err := checkIfSubscribedCatalog(cat) 316 if err != nil { 317 return nil, err 318 } 319 // The sync operation is only available for Catalog, not AdminCatalog. 320 // We use the embedded Catalog object for this purpose 321 catalogHref, err := cat.GetCatalogHref() 322 if err != nil || catalogHref == "" { 323 return nil, fmt.Errorf("empty catalog HREF for admin catalog %s", cat.AdminCatalog.Name) 324 } 325 err = cat.WaitForTasks() 326 if err != nil { 327 return nil, err 328 } 329 return elementLaunchSync(cat.client, catalogHref, "admin catalog") 330 } 331 332 // GetCatalogHref retrieves the regular catalog HREF from an admin catalog 333 func (cat *AdminCatalog) GetCatalogHref() (string, error) { 334 href := "" 335 for _, link := range cat.AdminCatalog.Link { 336 if link.Rel == "alternate" && link.Type == types.MimeCatalog { 337 href = link.HREF 338 break 339 } 340 } 341 if href == "" { 342 return "", fmt.Errorf("no regular Catalog HREF found for admin Catalog %s", cat.AdminCatalog.Name) 343 } 344 return href, nil 345 } 346 347 // QueryVappTemplateList returns a list of vApp templates for the given catalog 348 func (catalog *AdminCatalog) QueryVappTemplateList() ([]*types.QueryResultVappTemplateType, error) { 349 return queryVappTemplateListWithFilter(catalog.client, map[string]string{"catalogName": catalog.AdminCatalog.Name}) 350 } 351 352 // QueryMediaList retrieves a list of media items for the Admin Catalog 353 func (catalog *AdminCatalog) QueryMediaList() ([]*types.MediaRecordType, error) { 354 return queryMediaList(catalog.client, catalog.AdminCatalog.HREF) 355 } 356 357 // LaunchSynchronisationVappTemplates starts synchronisation of a list of vApp templates 358 func (cat *AdminCatalog) LaunchSynchronisationVappTemplates(nameList []string) ([]*Task, error) { 359 return launchSynchronisationVappTemplates(cat, nameList, true) 360 } 361 362 // launchSynchronisationVappTemplates waits for existing tasks to complete and then starts synchronisation for a list of vApp templates 363 // optionally checking for running tasks 364 // TODO: re-implement without the undocumented task-related fields 365 func launchSynchronisationVappTemplates(cat *AdminCatalog, nameList []string, checkForRunningTasks bool) ([]*Task, error) { 366 err := checkIfSubscribedCatalog(cat) 367 if err != nil { 368 return nil, err 369 } 370 util.Logger.Printf("[TRACE] launchSynchronisationVappTemplates - AdminCatalog '%s' - 'make_local_copy=%v]\n", cat.AdminCatalog.Name, cat.AdminCatalog.ExternalCatalogSubscription.LocalCopy) 371 var taskList []*Task 372 373 for _, element := range nameList { 374 var queryResultCatalogItem *types.QueryResultCatalogItemType 375 376 if checkForRunningTasks { 377 queryResultVappTemplate, err := cat.QueryVappTemplateWithName(element) 378 if err != nil { 379 return nil, err 380 } 381 err = checkIfTaskComplete(cat.client, queryResultVappTemplate.Task, queryResultVappTemplate.TaskStatus) 382 if err != nil { 383 return nil, err 384 } 385 queryResultCatalogItem = &types.QueryResultCatalogItemType{ 386 HREF: queryResultVappTemplate.CatalogItem, 387 ID: extractUuid(queryResultVappTemplate.CatalogItem), 388 Type: types.MimeCatalogItem, 389 Entity: queryResultVappTemplate.HREF, 390 EntityName: queryResultVappTemplate.Name, 391 EntityType: "vapptemplate", 392 Catalog: cat.AdminCatalog.HREF, 393 CatalogName: cat.AdminCatalog.Name, 394 Status: queryResultVappTemplate.Status, 395 Name: queryResultVappTemplate.Name, 396 } 397 } else { 398 queryResultCatalogItem, err = cat.QueryCatalogItem(element) 399 if err != nil { 400 return nil, fmt.Errorf("error retrieving catalog item %s: %s", element, err) 401 } 402 } 403 task, err := queryResultCatalogItemToCatalogItem(cat.client, queryResultCatalogItem).LaunchSync() 404 if err != nil { 405 return nil, err 406 } 407 if task != nil { 408 taskList = append(taskList, task) 409 } 410 } 411 return taskList, nil 412 } 413 414 // LaunchSynchronisationAllVappTemplates waits for existing tasks to complete and then starts synchronisation of all vApp templates for a given catalog 415 // TODO: re-implement without the undocumented task-related fields 416 func (cat *AdminCatalog) LaunchSynchronisationAllVappTemplates() ([]*Task, error) { 417 err := checkIfSubscribedCatalog(cat) 418 if err != nil { 419 return nil, err 420 } 421 util.Logger.Printf("[TRACE] AdminCatalog '%s' LaunchSynchronisationAllVappTemplates - 'make_local_copy=%v]\n", cat.AdminCatalog.Name, cat.AdminCatalog.ExternalCatalogSubscription.LocalCopy) 422 vappTemplatesList, err := cat.QueryVappTemplateList() 423 if err != nil { 424 return nil, err 425 } 426 var nameList []string 427 for _, element := range vappTemplatesList { 428 err = checkIfTaskComplete(cat.client, element.Task, element.TaskStatus) 429 if err != nil { 430 return nil, err 431 } 432 nameList = append(nameList, element.Name) 433 } 434 // Launch synchronisation for each item, without checking for running tasks, as it was already done in this function 435 return launchSynchronisationVappTemplates(cat, nameList, false) 436 } 437 438 func checkIfTaskComplete(client *Client, taskHref, taskStatus string) error { 439 complete := taskStatus == "" || isTaskCompleteOrError(taskStatus) 440 if !complete { 441 task, err := client.GetTaskById(taskHref) 442 if err != nil { 443 return err 444 } 445 err = task.WaitTaskCompletion() 446 if err != nil { 447 return err 448 } 449 } 450 return nil 451 } 452 453 func checkIfSubscribedCatalog(catalog *AdminCatalog) error { 454 err := catalog.Refresh() 455 if err != nil { 456 return err 457 } 458 if catalog.AdminCatalog.ExternalCatalogSubscription == nil || catalog.AdminCatalog.ExternalCatalogSubscription.Location == "" { 459 return fmt.Errorf("catalog '%s' is not subscribed", catalog.AdminCatalog.Name) 460 } 461 return nil 462 } 463 464 // LaunchSynchronisationMediaItems waits for existing tasks to complete and then starts synchronisation of a list of media items 465 // TODO: re-implement without the undocumented task-related fields 466 func (cat *AdminCatalog) LaunchSynchronisationMediaItems(nameList []string) ([]*Task, error) { 467 err := checkIfSubscribedCatalog(cat) 468 if err != nil { 469 return nil, err 470 } 471 util.Logger.Printf("[TRACE] AdminCatalog '%s' LaunchSynchronisationMediaItems\n", cat.AdminCatalog.Name) 472 var taskList []*Task 473 mediaList, err := cat.QueryMediaList() 474 if err != nil { 475 return nil, err 476 } 477 var actionList []string 478 479 var found = make(map[string]string) 480 for _, element := range mediaList { 481 if contains(element.Name, nameList) { 482 complete := element.TaskStatus == "" || isTaskCompleteOrError(element.TaskStatus) 483 if !complete { 484 if element.Task != "" { 485 task, err := cat.client.GetTaskById(element.Task) 486 if err != nil { 487 return nil, err 488 } 489 err = task.WaitTaskCompletion() 490 if err != nil { 491 return nil, err 492 } 493 } 494 } 495 util.Logger.Printf("scheduling for synchronisation Media item %s with catalog item HREF %s\n", element.Name, element.CatalogItem) 496 actionList = append(actionList, element.CatalogItem) 497 found[element.Name] = element.CatalogItem 498 } 499 } 500 if len(actionList) < len(nameList) { 501 var foundList []string 502 for k := range found { 503 foundList = append(foundList, k) 504 } 505 return nil, fmt.Errorf("%d names provided [%v] but %d actions scheduled [%v]", len(nameList), nameList, len(actionList), foundList) 506 } 507 for _, element := range actionList { 508 util.Logger.Printf("synchronising Media catalog item HREF %s\n", element) 509 catalogItem, err := cat.GetCatalogItemByHref(element) 510 if err != nil { 511 return nil, err 512 } 513 task, err := catalogItem.LaunchSync() 514 if err != nil { 515 return nil, err 516 } 517 if task != nil { 518 taskList = append(taskList, task) 519 } 520 } 521 return taskList, nil 522 } 523 524 // LaunchSynchronisationAllMediaItems waits for existing tasks to complete and then starts synchronisation of all media items for a given catalog 525 // TODO re-implement without the non-documented task-related fields 526 func (cat *AdminCatalog) LaunchSynchronisationAllMediaItems() ([]*Task, error) { 527 err := checkIfSubscribedCatalog(cat) 528 if err != nil { 529 return nil, err 530 } 531 util.Logger.Printf("[TRACE] AdminCatalog '%s' LaunchSynchronisationAllMediaItems\n", cat.AdminCatalog.Name) 532 var taskList []*Task 533 mediaList, err := cat.QueryMediaList() 534 if err != nil { 535 return nil, err 536 } 537 for _, element := range mediaList { 538 if isTaskRunning(element.TaskStatus) { 539 task, err := cat.client.GetTaskByHREF(element.Task) 540 if err != nil { 541 return nil, err 542 } 543 err = task.WaitTaskCompletion() 544 if err != nil { 545 return nil, err 546 } 547 } 548 catalogItem, err := cat.GetCatalogItemByHref(element.CatalogItem) 549 if err != nil { 550 return nil, err 551 } 552 task, err := catalogItem.LaunchSync() 553 if err != nil { 554 return nil, err 555 } 556 if task != nil { 557 taskList = append(taskList, task) 558 } 559 } 560 return taskList, nil 561 } 562 563 // GetCatalogItemByHref finds a CatalogItem by HREF 564 // On success, returns a pointer to the CatalogItem structure and a nil error 565 // On failure, returns a nil pointer and an error 566 func (cat *AdminCatalog) GetCatalogItemByHref(catalogItemHref string) (*CatalogItem, error) { 567 catItem := NewCatalogItem(cat.client) 568 569 _, err := cat.client.ExecuteRequest(catalogItemHref, http.MethodGet, 570 "", "error retrieving catalog item: %s", nil, catItem.CatalogItem) 571 if err != nil { 572 return nil, err 573 } 574 return catItem, nil 575 } 576 577 // UpdateSubscriptionParams modifies the subscription parameters of an already subscribed catalog 578 func (catalog *AdminCatalog) UpdateSubscriptionParams(params types.ExternalCatalogSubscription) error { 579 err := checkIfSubscribedCatalog(catalog) 580 if err != nil { 581 return err 582 } 583 var href string 584 for _, link := range catalog.AdminCatalog.Link { 585 if link.Rel == "subscribeToExternalCatalog" && link.Type == types.MimeSubscribeToExternalCatalog { 586 href = link.HREF 587 break 588 } 589 } 590 if href == "" { 591 return fmt.Errorf("catalog subscription link not found for catalog %s", catalog.AdminCatalog.Name) 592 } 593 _, err = catalog.client.ExecuteRequest(href, http.MethodPost, types.MimeAdminCatalog, 594 "error subscribing to catalog: %s", params, nil) 595 if err != nil { 596 return err 597 } 598 return catalog.Refresh() 599 } 600 601 // QueryTaskList retrieves a list of tasks associated to the Admin Catalog 602 func (catalog *AdminCatalog) QueryTaskList(filter map[string]string) ([]*types.QueryResultTaskRecordType, error) { 603 catalogHref, err := catalog.GetCatalogHref() 604 if err != nil { 605 return nil, err 606 } 607 if filter == nil { 608 filter = make(map[string]string) 609 } 610 filter["object"] = catalogHref 611 return catalog.client.QueryTaskList(filter) 612 } 613 614 // GetAdminCatalogByHref allows retrieving a catalog from HREF, without a fully qualified AdminOrg object 615 func (client *Client) GetAdminCatalogByHref(catalogHref string) (*AdminCatalog, error) { 616 catalogHref = strings.Replace(catalogHref, "/api/catalog", "/api/admin/catalog", 1) 617 618 cat := NewAdminCatalog(client) 619 620 _, err := client.ExecuteRequest(catalogHref, http.MethodGet, 621 "", "error retrieving catalog: %s", nil, cat.AdminCatalog) 622 623 if err != nil { 624 return nil, err 625 } 626 627 // Setting the catalog parent, necessary to handle the tenant context 628 org := NewAdminOrg(client) 629 for _, link := range cat.AdminCatalog.Link { 630 if link.Rel == "up" && link.Type == types.MimeAdminOrg { 631 _, err = client.ExecuteRequest(link.HREF, http.MethodGet, 632 "", "error retrieving parent Org: %s", nil, org.AdminOrg) 633 if err != nil { 634 return nil, fmt.Errorf("error retrieving catalog parent: %s", err) 635 } 636 break 637 } 638 } 639 640 cat.parent = org 641 return cat, nil 642 } 643 644 // QueryCatalogRecords given a catalog name, retrieves the catalogRecords that match its name 645 // Returns a list of catalog records for such name, empty list if none was found 646 func (client *Client) QueryCatalogRecords(name string, ctx TenantContext) ([]*types.CatalogRecord, error) { 647 util.Logger.Printf("[DEBUG] QueryCatalogRecords") 648 649 var filter string 650 if name != "" { 651 filter = fmt.Sprintf("name==%s", url.QueryEscape(name)) 652 } 653 654 var tenantHeaders map[string]string 655 656 if client.IsSysAdmin && ctx.OrgId != "" && ctx.OrgName != "" { 657 // Set tenant context headers just for the query 658 tenantHeaders = map[string]string{ 659 types.HeaderAuthContext: ctx.OrgName, 660 types.HeaderTenantContext: ctx.OrgId, 661 } 662 } 663 664 queryType := types.QtCatalog 665 666 results, err := client.cumulativeQueryWithHeaders(queryType, nil, map[string]string{ 667 "type": queryType, 668 "filter": filter, 669 "filterEncoded": "true", 670 }, tenantHeaders) 671 if err != nil { 672 return nil, err 673 } 674 675 catalogs := results.Results.CatalogRecord 676 677 util.Logger.Printf("[DEBUG] QueryCatalogRecords returned with : %#v (%d) and error: %v", catalogs, len(catalogs), err) 678 return catalogs, nil 679 } 680 681 // GetAdminCatalogById allows retrieving a catalog from ID, without a fully qualified AdminOrg object 682 func (client *Client) GetAdminCatalogById(catalogId string) (*AdminCatalog, error) { 683 href, err := url.JoinPath(client.VCDHREF.String(), "admin", "catalog", extractUuid(catalogId)) 684 if err != nil { 685 return nil, err 686 } 687 return client.GetAdminCatalogByHref(href) 688 } 689 690 // GetAdminCatalogByName allows retrieving a catalog from name, without a fully qualified AdminOrg object 691 func (client *Client) GetAdminCatalogByName(parentOrg, catalogName string) (*AdminCatalog, error) { 692 catalogs, err := queryCatalogList(client, nil) 693 if err != nil { 694 return nil, err 695 } 696 var parentOrgs []string 697 for _, cat := range catalogs { 698 if cat.Name == catalogName && cat.OrgName == parentOrg { 699 return client.GetAdminCatalogByHref(cat.HREF) 700 } 701 if cat.Name == catalogName { 702 parentOrgs = append(parentOrgs, cat.OrgName) 703 } 704 } 705 parents := "" 706 if len(parentOrgs) > 0 { 707 parents = fmt.Sprintf(" - Found catalog %s in Orgs %v", catalogName, parentOrgs) 708 } 709 return nil, fmt.Errorf("no catalog '%s' found in Org %s%s", catalogName, parentOrg, parents) 710 }