github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/catalog_subscription_test.go (about) 1 //go:build catalog || functional || ALL 2 3 /* 4 * Copyright 2019 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. 5 */ 6 7 package govcd 8 9 import ( 10 "fmt" 11 "net/url" 12 "strings" 13 "time" 14 15 "github.com/vmware/go-vcloud-director/v2/types/v56" 16 . "gopkg.in/check.v1" 17 ) 18 19 type subscriptionTestData struct { 20 fromOrg *AdminOrg 21 toOrg *AdminOrg 22 ovaPath string 23 mediaPath string 24 localCopy bool 25 storageProfile types.CatalogStorageProfiles 26 uploadWhen string 27 preservePublishingCatalog bool 28 asynchronousSubscription bool 29 } 30 31 // Test_SubscribedCatalog tests four scenarios of Catalog subscription 32 // All cases use a publishing catalog in one Org and a subscribing catalog 33 // in a different Org. 34 // The scenarios are a combination of these two facts: 35 // * whether the subscribing catalog was created before or after the publishing catalog was filled 36 // * whether the subscribing catalog enabled automatic downloads (localCopy) 37 // 38 // To see the inner working of the test components, you may run it as follows: 39 // $ export GOVCD_TASK_MONITOR=simple_show 40 // $ go test -tags catalog -check.f Test_SubscribedCatalog -vcd-verbose -check.vv -timeout 0 41 // When running this way, you will see the tasks originated by the catalogs and the ones started by the catalog items 42 func (vcd *TestVCD) Test_SubscribedCatalog(check *C) { 43 vcd.skipIfNotSysAdmin(check) 44 fromOrg, err := vcd.client.GetAdminOrgByName(vcd.config.VCD.Org) 45 check.Assert(err, IsNil) 46 toOrg, err := vcd.client.GetAdminOrgByName(vcd.config.VCD.Org + "-1") 47 check.Assert(err, IsNil) 48 49 if toOrg.AdminOrg.Vdcs == nil || len(toOrg.AdminOrg.Vdcs.Vdcs) == 0 { 50 check.Skip(fmt.Sprintf("receiving org %s does not have any storage", toOrg.AdminOrg.Name)) 51 } 52 // TODO: remove this workaround when support for 10.3.3 is dropped 53 // See Test_PublishToExternalOrganizations for details 54 fromOrg.AdminOrg.OrgSettings.OrgGeneralSettings.CanPublishCatalogs = true 55 fromOrg.AdminOrg.OrgSettings.OrgGeneralSettings.CanPublishExternally = true 56 _, err = fromOrg.Update() 57 58 check.Assert(err, IsNil) 59 vdc, err := fromOrg.GetVDCByName(vcd.config.VCD.Nsxt.Vdc, false) 60 check.Assert(err, IsNil) 61 62 storageProfile, err := vdc.FindStorageProfileReference(vcd.config.VCD.StorageProfile.SP1) 63 check.Assert(err, IsNil) 64 createStorageProfiles := types.CatalogStorageProfiles{VdcStorageProfile: []*types.Reference{&storageProfile}} 65 66 testSubscribedCatalog(subscriptionTestData{ 67 fromOrg: fromOrg, 68 toOrg: toOrg, 69 ovaPath: vcd.config.OVA.OvaPath, 70 mediaPath: vcd.config.Media.MediaPath, 71 localCopy: false, 72 storageProfile: createStorageProfiles, 73 uploadWhen: "after_subscription", 74 asynchronousSubscription: true, 75 }, check) 76 77 testSubscribedCatalog(subscriptionTestData{ 78 fromOrg: fromOrg, 79 toOrg: toOrg, 80 ovaPath: vcd.config.OVA.OvaPath, 81 mediaPath: vcd.config.Media.MediaPath, 82 localCopy: true, 83 storageProfile: createStorageProfiles, 84 uploadWhen: "after_subscription", 85 preservePublishingCatalog: true, 86 asynchronousSubscription: true, 87 }, check) 88 89 // For the tests where the items are uploaded before subscription, we can keep the publishing catalog 90 // from the previous test 91 testSubscribedCatalog(subscriptionTestData{ 92 fromOrg: fromOrg, 93 toOrg: toOrg, 94 ovaPath: vcd.config.OVA.OvaPath, 95 mediaPath: vcd.config.Media.MediaPath, 96 localCopy: false, 97 storageProfile: createStorageProfiles, 98 uploadWhen: "before_subscription", 99 preservePublishingCatalog: true, 100 }, check) 101 testSubscribedCatalog(subscriptionTestData{ 102 fromOrg: fromOrg, 103 toOrg: toOrg, 104 ovaPath: vcd.config.OVA.OvaPath, 105 mediaPath: vcd.config.Media.MediaPath, 106 localCopy: true, 107 storageProfile: createStorageProfiles, 108 uploadWhen: "before_subscription", 109 preservePublishingCatalog: false, // at the last subtest, we remove the publishing catalog 110 }, check) 111 112 } 113 114 func uploadTestItems(org *AdminOrg, catalogName, templatePath, mediaPath string, numTemplates, numMedia int) error { 115 var taskList []*Task 116 117 catalog, err := org.GetCatalogByName(catalogName, true) 118 if err != nil { 119 return fmt.Errorf("catalog %s not found: %s", catalogName, err) 120 } 121 122 for i := 1; i <= numTemplates; i++ { 123 templateName := fmt.Sprintf("test-vt-%d", i) 124 uploadTask, err := catalog.UploadOvf(templatePath, templateName, "upload from test", 1024) 125 if err != nil { 126 return err 127 } 128 taskList = append(taskList, uploadTask.Task) 129 } 130 for i := 1; i <= numMedia; i++ { 131 mediaName := fmt.Sprintf("test_media-%d", i) 132 uploadTask, err := catalog.UploadMediaImage(mediaName, "upload from test", mediaPath, 1024) 133 if err != nil { 134 return err 135 } 136 taskList = append(taskList, uploadTask.Task) 137 } 138 _, err = WaitTaskListCompletionMonitor(taskList, testMonitor) 139 fmt.Println() 140 return err 141 } 142 143 func testSubscribedCatalog(testData subscriptionTestData, check *C) { 144 145 startSubtest := time.Now() 146 drawHeader := func(char, msg string) { 147 fmt.Println(strings.Repeat(char, 80)) 148 fmt.Printf("%s %s\n", char, msg) 149 } 150 drawHeader("*", fmt.Sprintf("START: upload %s - local copy: %v", testData.uploadWhen, testData.localCopy)) 151 152 fromOrg := testData.fromOrg 153 toOrg := testData.toOrg 154 155 publishingCatalogName := "Publisher" 156 subscribingCatalogName := "Subscriber" 157 158 var fromCatalog *AdminCatalog 159 var err error 160 fromCatalog, err = fromOrg.GetAdminCatalogByName(publishingCatalogName, true) 161 if err == nil { 162 drawHeader("-", "publishing catalog retrieved from previous test") 163 } else { 164 drawHeader("-", "creating publishing catalog") 165 fromCatalog, err = fromOrg.CreateCatalogWithStorageProfile(publishingCatalogName, "publisher catalog", &testData.storageProfile) 166 check.Assert(err, IsNil) 167 AddToCleanupList(publishingCatalogName, "catalog", fromOrg.AdminOrg.Name, check.TestName()) 168 } 169 170 subscriptionPassword := "superUnknown" 171 err = fromCatalog.PublishToExternalOrganizations(types.PublishExternalCatalogParams{ 172 IsPublishedExternally: addrOf(true), 173 Password: subscriptionPassword, 174 IsCachedEnabled: addrOf(true), 175 PreserveIdentityInfoFlag: addrOf(true), 176 }) 177 check.Assert(err, IsNil) 178 179 uploadItemsIf := func(wanted string) { 180 if wanted != testData.uploadWhen { 181 return 182 } 183 howManyTemplates := 3 184 howManyMediaItems := 3 185 publishedCatalogItems, err := fromCatalog.QueryCatalogItemList() 186 if err == nil && len(publishedCatalogItems) == (howManyMediaItems+howManyTemplates) { 187 return 188 } 189 drawHeader("-", fmt.Sprintf("uploading catalog items - %s", wanted)) 190 err = uploadTestItems(fromOrg, fromCatalog.AdminCatalog.Name, testData.ovaPath, testData.mediaPath, howManyTemplates, howManyMediaItems) 191 check.Assert(err, IsNil) 192 } 193 err = fromCatalog.Refresh() 194 check.Assert(err, IsNil) 195 196 check.Assert(fromCatalog.AdminCatalog.PublishExternalCatalogParams, NotNil) 197 check.Assert(fromCatalog.AdminCatalog.PublishExternalCatalogParams.CatalogPublishedUrl, Not(Equals), "") 198 199 uploadItemsIf("before_subscription") 200 err = fromCatalog.Refresh() 201 check.Assert(err, IsNil) 202 203 subscriptionUrl, err := fromCatalog.FullSubscriptionUrl() 204 check.Assert(err, IsNil) 205 206 subscriptionParams := types.ExternalCatalogSubscription{ 207 SubscribeToExternalFeeds: true, 208 Location: subscriptionUrl, 209 Password: subscriptionPassword, 210 LocalCopy: testData.localCopy, 211 } 212 213 var toCatalog *AdminCatalog 214 testSubscribedCatalogWithInvalidParameters(toOrg, subscriptionParams, subscribingCatalogName, subscriptionPassword, testData.localCopy, check) 215 if testData.asynchronousSubscription { 216 drawHeader("-", "creating subscribed catalog asynchronously") 217 // With asynchronous subscription the catalog starts the subscription but does not report its state, which is 218 // monitored by its internal Task 219 toCatalog, err = toOrg.CreateCatalogFromSubscriptionAsync( 220 subscriptionParams, // params 221 nil, // storage profile 222 subscribingCatalogName, // catalog name 223 subscriptionPassword, // password 224 testData.localCopy) // local copy 225 } else { 226 drawHeader("-", "creating subscribed catalog and waiting for completion") 227 toCatalog, err = toOrg.CreateCatalogFromSubscription( 228 subscriptionParams, // params 229 nil, // storage profile 230 subscribingCatalogName, // catalog name 231 subscriptionPassword, // password 232 testData.localCopy, // local copy 233 10*time.Minute) // timeout 234 } 235 check.Assert(err, IsNil) 236 AddToCleanupList(subscribingCatalogName, "catalog", toOrg.AdminOrg.Name, check.TestName()) 237 238 if testData.asynchronousSubscription { 239 err = toCatalog.Refresh() 240 check.Assert(err, IsNil) 241 if ResourceInProgress(toCatalog.AdminCatalog.Tasks) { 242 fmt.Println("catalog subscription tasks still in progress") 243 for _, task := range toCatalog.AdminCatalog.Tasks.Task { 244 testMonitor(task) 245 } 246 } else { 247 fmt.Println("catalog subscription tasks complete") 248 } 249 } 250 251 uploadItemsIf("after_subscription") 252 253 // If the catalog items were uploaded before the catalog subscription, we don't need to 254 // synchronise, as the subscription would have got at least the list of items 255 if testData.uploadWhen != "before_subscription" { 256 drawHeader("-", "synchronising catalog") 257 err = toCatalog.Sync() 258 check.Assert(err, IsNil) 259 } 260 261 publishedCatalogItems, err := fromCatalog.QueryCatalogItemList() 262 check.Assert(err, IsNil) 263 subscribedCatalogItems, err := toCatalog.QueryCatalogItemList() 264 check.Assert(err, IsNil) 265 fmt.Printf("Catalog items after catalog sync: %d\n", len(subscribedCatalogItems)) 266 publishedVappTemplates, err := fromCatalog.QueryVappTemplateList() 267 check.Assert(err, IsNil) 268 subscribedVappTemplates, err := toCatalog.QueryVappTemplateList() 269 check.Assert(err, IsNil) 270 publishedMediaItems, err := fromCatalog.QueryMediaList() 271 check.Assert(err, IsNil) 272 subscribedMediaItems, err := toCatalog.QueryMediaList() 273 check.Assert(err, IsNil) 274 275 fmt.Printf("vApp template after catalog sync %d\n", len(subscribedVappTemplates)) 276 fmt.Printf("media item after catalog sync %d\n", len(subscribedMediaItems)) 277 278 check.Assert(len(subscribedCatalogItems), Equals, len(publishedCatalogItems)) 279 check.Assert(len(subscribedVappTemplates), Equals, len(publishedVappTemplates)) 280 check.Assert(len(subscribedMediaItems), Equals, len(publishedMediaItems)) 281 282 if testData.localCopy && testData.uploadWhen == "before_subscription" { 283 // we should have all the contents here if the data was available early 284 // and the subscribed catalog uses automatic download 285 retrieveCatalogItems(toCatalog, subscribedCatalogItems, check) 286 } 287 288 // Synchronising all vApp templates and media items. If the subscription includes local copy, 289 // the synchronisation has alredy happened, and this extra call is very quick (~5 seconds) 290 drawHeader("-", "synchronising vApp templates and media items") 291 tasksVappTemplates, err := toCatalog.LaunchSynchronisationAllVappTemplates() 292 check.Assert(err, IsNil) 293 tasksMediaItems, err := toCatalog.LaunchSynchronisationAllMediaItems() 294 check.Assert(err, IsNil) 295 296 // Wait for all synchronisation tasks to end 297 var allTasks []*Task 298 allTasks = append(allTasks, tasksVappTemplates...) 299 allTasks = append(allTasks, tasksMediaItems...) 300 _, err = WaitTaskListCompletionMonitor(allTasks, testMonitor) 301 if !testVerbose { 302 fmt.Println() 303 } 304 check.Assert(err, IsNil) 305 306 // after a full synchronisation, all data should be available under every condition 307 retrieveCatalogItems(toCatalog, subscribedCatalogItems, check) 308 309 startDelete := time.Now() 310 err = toCatalog.Delete(true, true) 311 check.Assert(err, IsNil) 312 fmt.Printf("subscribed catalog deletion done in %s\n", time.Since(startDelete)) 313 startDelete = time.Now() 314 if !testData.preservePublishingCatalog { 315 err = fromCatalog.Delete(true, true) 316 check.Assert(err, IsNil) 317 fmt.Printf("published catalog deletion done in %s\n", time.Since(startDelete)) 318 } 319 drawHeader("=", fmt.Sprintf("END: upload %s - local copy: %v - Time taken: %s", testData.uploadWhen, testData.localCopy, time.Since(startSubtest))) 320 } 321 322 func retrieveCatalogItems(toCatalog *AdminCatalog, subscribed []*types.QueryResultCatalogItemType, check *C) { 323 for _, item := range subscribed { 324 catalogItem, err := toCatalog.GetCatalogItemByHref(item.HREF) 325 check.Assert(err, IsNil) 326 switch catalogItem.CatalogItem.Entity.Type { 327 case types.MimeVAppTemplate: 328 vAppTemplate, err := catalogItem.GetVAppTemplate() 329 check.Assert(err, IsNil) 330 check.Assert(vAppTemplate.VAppTemplate.HREF, Equals, catalogItem.CatalogItem.Entity.HREF) 331 case types.MimeMediaItem: 332 mediaItem, err := toCatalog.GetMediaByHref(catalogItem.CatalogItem.Entity.HREF) 333 check.Assert(err, IsNil) 334 check.Assert(extractUuid(mediaItem.Media.ID), Equals, extractUuid(catalogItem.CatalogItem.Entity.HREF)) 335 } 336 } 337 } 338 339 func testMonitor(task *types.Task) { 340 if testVerbose { 341 fmt.Printf("task %s - owner %s - operation %s - status %s - progress %d\n", task.ID, task.Owner.Name, task.Operation, task.Status, task.Progress) 342 } else { 343 marker := "." 344 if task.Status == "success" { 345 marker = "+" 346 } 347 if task.Status == "error" { 348 marker = "-" 349 } 350 fmt.Print(marker) 351 } 352 } 353 354 func testSubscribedCatalogWithInvalidParameters(org *AdminOrg, subscription types.ExternalCatalogSubscription, 355 name, password string, localCopy bool, check *C) { 356 357 uuid := extractUuid(subscription.Location) 358 params := subscription 359 params.Location = strings.Replace(params.Location, uuid, "deadbeef-d72f-4a21-a4d2-4dc9e0b36555", 1) 360 // Use a valid host with invalid UUID 361 _, err := org.CreateCatalogFromSubscriptionAsync(params, nil, name, password, localCopy) 362 check.Assert(err, ErrorMatches, ".*RESOURCE_NOT_FOUND.*") 363 364 newUrl, err := url.Parse(subscription.Location) 365 check.Assert(err, IsNil) 366 367 params = subscription 368 params.Location = strings.Replace(params.Location, newUrl.Host, "fake.example.com", 1) 369 // use an invalid host 370 _, err = org.CreateCatalogFromSubscriptionAsync(params, nil, name, password, localCopy) 371 check.Assert(err, ErrorMatches, ".*INVALID_URL_OR_PASSWORD.*") 372 373 params = subscription 374 params.Location = "not-an-URL" 375 // use an invalid URL 376 _, err = org.CreateCatalogFromSubscriptionAsync(params, nil, name, password, localCopy) 377 check.Assert(err, ErrorMatches, ".*UNKNOWN_ERROR.*") 378 }