github.com/dean7474/operator-registry@v1.21.1-0.20220418203638-d4717f98c2e5/test/e2e/opm_test.go (about) 1 package e2e_test 2 3 import ( 4 "context" 5 "database/sql" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "reflect" 12 13 . "github.com/onsi/ginkgo" 14 . "github.com/onsi/gomega" 15 "github.com/otiai10/copy" 16 "github.com/sirupsen/logrus" 17 "k8s.io/apimachinery/pkg/util/rand" 18 19 "github.com/operator-framework/operator-registry/pkg/containertools" 20 "github.com/operator-framework/operator-registry/pkg/image" 21 "github.com/operator-framework/operator-registry/pkg/image/containerdregistry" 22 "github.com/operator-framework/operator-registry/pkg/image/execregistry" 23 "github.com/operator-framework/operator-registry/pkg/lib/bundle" 24 "github.com/operator-framework/operator-registry/pkg/lib/indexer" 25 lregistry "github.com/operator-framework/operator-registry/pkg/lib/registry" 26 "github.com/operator-framework/operator-registry/pkg/registry" 27 "github.com/operator-framework/operator-registry/pkg/sqlite" 28 ) 29 30 var ( 31 packageName = "prometheus" 32 channels = "preview" 33 defaultChannel = "preview" 34 35 bundlePath1 = "manifests/prometheus/0.14.0" 36 bundlePath2 = "manifests/prometheus/0.15.0" 37 bundlePath3 = "manifests/prometheus/0.22.2" 38 39 bundleTag1 = rand.String(6) 40 bundleTag2 = rand.String(6) 41 bundleTag3 = rand.String(6) 42 indexTag1 = rand.String(6) 43 indexTag2 = rand.String(6) 44 indexTag3 = rand.String(6) 45 46 bundleImage = dockerHost + "/olmtest/e2e-bundle" 47 indexImage = dockerHost + "/olmtest/e2e-index" 48 indexImage1 = dockerHost + "/olmtest/e2e-index:" + indexTag1 49 indexImage2 = dockerHost + "/olmtest/e2e-index:" + indexTag2 50 indexImage3 = dockerHost + "/olmtest/e2e-index:" + indexTag3 51 52 // publishedIndex is an index used to check for regressions in opm's behavior. 53 publishedIndex = os.Getenv("PUBLISHED_INDEX") 54 ) 55 56 type bundleLocation struct { 57 image, path string 58 } 59 60 type bundleLocations []bundleLocation 61 62 func (bl bundleLocations) images() []string { 63 images := make([]string, len(bl)) 64 for i, b := range bl { 65 images[i] = b.image 66 } 67 68 return images 69 } 70 71 func inTemporaryBuildContext(f func() error) (rerr error) { 72 td, err := ioutil.TempDir(".", "opm-") 73 if err != nil { 74 return err 75 } 76 err = copy.Copy("../../manifests", filepath.Join(td, "manifests")) 77 if err != nil { 78 return err 79 } 80 wd, err := os.Getwd() 81 if err != nil { 82 return err 83 } 84 err = os.Chdir(td) 85 if err != nil { 86 return err 87 } 88 defer func() { 89 err := os.Chdir(wd) 90 if rerr == nil { 91 rerr = err 92 } 93 }() 94 return f() 95 } 96 97 func buildIndexWith(containerTool, fromIndexImage, toIndexImage string, bundleImages []string, mode registry.Mode, overwriteLatest bool) error { 98 logger := logrus.WithFields(logrus.Fields{"bundles": bundleImages}) 99 indexAdder := indexer.NewIndexAdder(containertools.NewContainerTool(containerTool, containertools.NoneTool), containertools.NewContainerTool(containerTool, containertools.NoneTool), logger) 100 101 request := indexer.AddToIndexRequest{ 102 Generate: false, 103 FromIndex: fromIndexImage, 104 BinarySourceImage: "", 105 OutDockerfile: "", 106 Tag: toIndexImage, 107 Mode: mode, 108 Bundles: bundleImages, 109 Permissive: false, 110 Overwrite: overwriteLatest, 111 SkipTLSVerify: *skipTLSForRegistry, 112 PlainHTTP: *useHTTPforRegistry, 113 } 114 115 return indexAdder.AddToIndex(request) 116 } 117 118 func buildFromIndexWith(containerTool string) error { 119 bundles := []string{ 120 bundleImage + ":" + bundleTag3, 121 } 122 logger := logrus.WithFields(logrus.Fields{"bundles": bundles}) 123 indexAdder := indexer.NewIndexAdder(containertools.NewContainerTool(containerTool, containertools.NoneTool), containertools.NewContainerTool(containerTool, containertools.NoneTool), logger) 124 125 request := indexer.AddToIndexRequest{ 126 Generate: false, 127 FromIndex: indexImage1, 128 BinarySourceImage: "", 129 OutDockerfile: "", 130 Tag: indexImage2, 131 Bundles: bundles, 132 Permissive: false, 133 } 134 135 return indexAdder.AddToIndex(request) 136 } 137 138 // TODO(djzager): make this more complete than what should be a simple no-op 139 func pruneIndexWith(containerTool string) error { 140 logger := logrus.WithFields(logrus.Fields{"packages": packageName}) 141 indexAdder := indexer.NewIndexPruner(containertools.NewContainerTool(containerTool, containertools.NoneTool), logger) 142 143 request := indexer.PruneFromIndexRequest{ 144 Generate: false, 145 FromIndex: indexImage2, 146 BinarySourceImage: "", 147 OutDockerfile: "", 148 Tag: indexImage3, 149 Packages: []string{packageName}, 150 Permissive: false, 151 } 152 153 return indexAdder.PruneFromIndex(request) 154 } 155 156 func pushWith(containerTool, image string) error { 157 dockerpush := exec.Command(containerTool, "push", image) 158 dockerpush.Stderr = GinkgoWriter 159 dockerpush.Stdout = GinkgoWriter 160 return dockerpush.Run() 161 } 162 163 func exportPackageWith(containerTool string) error { 164 packages := []string{packageName} 165 logger := logrus.WithFields(logrus.Fields{"package": packages}) 166 indexExporter := indexer.NewIndexExporter(containertools.NewContainerTool(containerTool, containertools.NoneTool), logger) 167 168 request := indexer.ExportFromIndexRequest{ 169 Index: indexImage2, 170 Packages: packages, 171 DownloadPath: "downloaded", 172 ContainerTool: containertools.NewContainerTool(containerTool, containertools.NoneTool), 173 SkipTLSVerify: *skipTLSForRegistry, 174 PlainHTTP: *useHTTPforRegistry, 175 } 176 177 return indexExporter.ExportFromIndex(request) 178 } 179 180 func exportIndexImageWith(containerTool string) error { 181 182 logger := logrus.NewEntry(logrus.New()) 183 indexExporter := indexer.NewIndexExporter(containertools.NewContainerTool(containerTool, containertools.NoneTool), logger) 184 185 request := indexer.ExportFromIndexRequest{ 186 Index: indexImage2, 187 Packages: []string{}, 188 DownloadPath: "downloaded", 189 ContainerTool: containertools.NewContainerTool(containerTool, containertools.NoneTool), 190 SkipTLSVerify: *skipTLSForRegistry, 191 PlainHTTP: *useHTTPforRegistry, 192 } 193 194 return indexExporter.ExportFromIndex(request) 195 } 196 197 func initialize() error { 198 tmpDB, err := ioutil.TempFile("./", "index_tmp.db") 199 if err != nil { 200 return err 201 } 202 defer os.Remove(tmpDB.Name()) 203 204 db, err := sqlite.Open(tmpDB.Name()) 205 if err != nil { 206 return err 207 } 208 defer db.Close() 209 210 dbLoader, err := sqlite.NewSQLLiteLoader(db) 211 if err != nil { 212 return err 213 } 214 if err := dbLoader.Migrate(context.TODO()); err != nil { 215 return err 216 } 217 218 loader := sqlite.NewSQLLoaderForDirectory(dbLoader, "downloaded") 219 return loader.Populate() 220 } 221 222 var _ = Describe("opm", func() { 223 IncludeSharedSpecs := func(containerTool string) { 224 It("builds and validates a bundle image", func() { 225 By("building bundle") 226 img := bundleImage + ":" + bundleTag3 227 err := inTemporaryBuildContext(func() error { 228 return bundle.BuildFunc(bundlePath3, "", img, containerTool, packageName, channels, defaultChannel, false) 229 }) 230 Expect(err).NotTo(HaveOccurred()) 231 232 By("pushing bundle") 233 Expect(pushWith(containerTool, img)).To(Succeed()) 234 235 By("pulling bundle") 236 logger := logrus.WithFields(logrus.Fields{"image": img}) 237 tool := containertools.NewContainerTool(containerTool, containertools.NoneTool) 238 var registry image.Registry 239 switch tool { 240 case containertools.PodmanTool, containertools.DockerTool: 241 registry, err = execregistry.NewRegistry(tool, logger) 242 case containertools.NoneTool: 243 registry, err = containerdregistry.NewRegistry(containerdregistry.WithLog(logger)) 244 default: 245 err = fmt.Errorf("unrecognized container-tool option: %s", containerTool) 246 } 247 Expect(err).NotTo(HaveOccurred()) 248 249 unpackDir, err := ioutil.TempDir(".", bundleTag3) 250 Expect(err).NotTo(HaveOccurred()) 251 validator := bundle.NewImageValidator(registry, logger) 252 Expect(validator.PullBundleImage(img, unpackDir)).To(Succeed()) 253 254 By("validating bundle format") 255 Expect(validator.ValidateBundleFormat(unpackDir)).To(Succeed()) 256 257 By("validating bundle content") 258 manifestsDir := filepath.Join(unpackDir, bundle.ManifestsDir) 259 Expect(validator.ValidateBundleContent(manifestsDir)).To(Succeed()) 260 Expect(os.RemoveAll(unpackDir)).To(Succeed()) 261 }) 262 263 It("builds and manipulates bundle and index images", func() { 264 By("building bundles") 265 bundles := bundleLocations{ 266 {bundleImage + ":" + bundleTag1, bundlePath1}, 267 {bundleImage + ":" + bundleTag2, bundlePath2}, 268 {bundleImage + ":" + bundleTag3, bundlePath3}, 269 } 270 var err error 271 for _, b := range bundles { 272 err = inTemporaryBuildContext(func() error { 273 return bundle.BuildFunc(b.path, "", b.image, containerTool, packageName, channels, defaultChannel, false) 274 }) 275 Expect(err).NotTo(HaveOccurred()) 276 } 277 278 By("pushing bundles") 279 for _, b := range bundles { 280 Expect(pushWith(containerTool, b.image)).NotTo(HaveOccurred()) 281 } 282 283 By("building an index") 284 err = buildIndexWith(containerTool, "", indexImage1, bundles[:2].images(), registry.ReplacesMode, false) 285 Expect(err).NotTo(HaveOccurred()) 286 287 By("pushing an index") 288 err = pushWith(containerTool, indexImage1) 289 Expect(err).NotTo(HaveOccurred()) 290 291 By("building from an index") 292 err = buildFromIndexWith(containerTool) 293 Expect(err).NotTo(HaveOccurred()) 294 295 By("pushing an index") 296 err = pushWith(containerTool, indexImage2) 297 Expect(err).NotTo(HaveOccurred()) 298 299 By("pruning an index") 300 err = pruneIndexWith(containerTool) 301 Expect(err).NotTo(HaveOccurred()) 302 303 By("pushing an index") 304 err = pushWith(containerTool, indexImage3) 305 Expect(err).NotTo(HaveOccurred()) 306 307 By("exporting a package from an index to disk") 308 err = exportPackageWith(containerTool) 309 Expect(err).NotTo(HaveOccurred()) 310 311 By("loading manifests from a directory") 312 err = initialize() 313 Expect(err).NotTo(HaveOccurred()) 314 315 // clean and try again with containerd 316 err = os.RemoveAll("downloaded") 317 Expect(err).NotTo(HaveOccurred()) 318 319 By("exporting a package from an index to disk with containerd") 320 err = exportPackageWith(containertools.NoneTool.String()) 321 Expect(err).NotTo(HaveOccurred()) 322 323 By("loading manifests from a containerd-extracted directory") 324 err = initialize() 325 Expect(err).NotTo(HaveOccurred()) 326 327 // clean containerd-extracted directory 328 err = os.RemoveAll("downloaded") 329 Expect(err).NotTo(HaveOccurred()) 330 331 By("exporting an entire index to disk") 332 err = exportIndexImageWith(containerTool) 333 Expect(err).NotTo(HaveOccurred()) 334 335 By("loading manifests from a directory") 336 err = initialize() 337 Expect(err).NotTo(HaveOccurred()) 338 }) 339 340 It("build bundles and index via inference", func() { 341 342 bundles := bundleLocations{ 343 {bundleImage + ":" + rand.String(6), "./testdata/aqua/0.0.1"}, 344 {bundleImage + ":" + rand.String(6), "./testdata/aqua/0.0.2"}, 345 {bundleImage + ":" + rand.String(6), "./testdata/aqua/1.0.0"}, 346 {bundleImage + ":" + rand.String(6), "./testdata/aqua/1.0.1"}, 347 } 348 349 By("building bundles") 350 for _, b := range bundles { 351 td, err := ioutil.TempDir(".", "opm-") 352 Expect(err).NotTo(HaveOccurred()) 353 defer os.RemoveAll(td) 354 355 err = bundle.BuildFunc(b.path, td, b.image, containerTool, "", "", "", true) 356 Expect(err).NotTo(HaveOccurred()) 357 } 358 359 By("pushing bundles") 360 for _, b := range bundles { 361 Expect(pushWith(containerTool, b.image)).NotTo(HaveOccurred()) 362 } 363 364 By("building an index") 365 indexImage := indexImage + ":" + rand.String(6) 366 err := buildIndexWith(containerTool, "", indexImage, bundles.images(), registry.ReplacesMode, false) 367 Expect(err).NotTo(HaveOccurred()) 368 }) 369 It("build index without bundles", func() { 370 indexImage := indexImage + ":" + rand.String(6) 371 By("building an index") 372 err := buildIndexWith(containerTool, "", indexImage, []string{}, registry.ReplacesMode, true) 373 Expect(err).NotTo(HaveOccurred()) 374 }) 375 376 PIt("can overwrite existing bundles in an index", func() { 377 // TODO fix regression overwriting existing bundles in an index 378 bundles := bundleLocations{ 379 {bundleImage + ":" + rand.String(6), "./testdata/aqua/0.0.1"}, 380 {bundleImage + ":" + rand.String(6), "./testdata/aqua/0.0.2"}, 381 {bundleImage + ":" + rand.String(6), "./testdata/aqua/1.0.0"}, 382 {bundleImage + ":" + rand.String(6), "./testdata/aqua/1.0.1"}, 383 {bundleImage + ":" + rand.String(6), "./testdata/aqua/1.0.1-overwrite"}, 384 } 385 386 for _, b := range bundles { 387 td, err := ioutil.TempDir(".", "opm-") 388 Expect(err).NotTo(HaveOccurred()) 389 defer os.RemoveAll(td) 390 391 err = bundle.BuildFunc(b.path, td, b.image, containerTool, "", "", "", true) 392 Expect(err).NotTo(HaveOccurred()) 393 } 394 395 By("pushing bundles") 396 for _, b := range bundles { 397 Expect(pushWith(containerTool, b.image)).NotTo(HaveOccurred()) 398 } 399 400 indexImage := indexImage + ":" + rand.String(6) 401 By("adding net-new bundles to an index") 402 err := buildIndexWith(containerTool, "", indexImage, bundles[:4].images(), registry.ReplacesMode, true) // 0.0.1, 0.0.2, 1.0.0, 1.0.1 403 Expect(err).NotTo(HaveOccurred()) 404 Expect(pushWith(containerTool, indexImage)).NotTo(HaveOccurred()) 405 406 By("failing to overwrite a non-latest bundle") 407 nextIndex := indexImage + "-next" 408 err = buildIndexWith(containerTool, indexImage, nextIndex, bundles[1:2].images(), registry.ReplacesMode, true) // 0.0.2 409 Expect(err).To(HaveOccurred()) 410 411 By("failing to overwrite in a non-replace mode") 412 err = buildIndexWith(containerTool, indexImage, nextIndex, bundles[4:].images(), registry.SemVerMode, true) // 1.0.1-overwrite 413 Expect(err).To(HaveOccurred()) 414 415 By("overwriting the latest bundle in an index") 416 err = buildIndexWith(containerTool, indexImage, nextIndex, bundles[4:].images(), registry.ReplacesMode, true) // 1.0.1-overwrite 417 Expect(err).NotTo(HaveOccurred()) 418 }) 419 420 It("doesn't change published content on overwrite", func() { 421 if publishedIndex == "" { 422 Skip("Set the PUBLISHED_INDEX environment variable to enable this test") 423 } 424 425 logger := logrus.NewEntry(logrus.StandardLogger()) 426 logger.Logger.SetLevel(logrus.WarnLevel) 427 tool := containertools.NewContainerTool(containerTool, containertools.NoneTool) 428 imageIndexer := indexer.ImageIndexer{ 429 PullTool: tool, 430 Logger: logger, 431 } 432 dbFile, err := imageIndexer.ExtractDatabase(".", publishedIndex, "", *skipTLSForRegistry, *useHTTPforRegistry) 433 Expect(err).NotTo(HaveOccurred(), "error extracting registry db") 434 435 db, err := sql.Open("sqlite3", fmt.Sprintf("file:%s", dbFile)) 436 Expect(err).NotTo(HaveOccurred(), "Error reading db file") 437 438 querier := sqlite.NewSQLLiteQuerierFromDb(db) 439 440 graphLoader, err := sqlite.NewSQLGraphLoaderFromDB(db) 441 Expect(err).NotTo(HaveOccurred(), "Error reading db file") 442 443 packages, err := querier.ListPackages(context.TODO()) 444 Expect(err).NotTo(HaveOccurred(), "Error listing packages") 445 446 var errs []error 447 448 adder := lregistry.NewRegistryAdder(logger) 449 for _, pkg := range packages { 450 existing, err := graphLoader.Generate(pkg) 451 Expect(err).NotTo(HaveOccurred(), "Error generating graph for package %s", pkg, existing) 452 453 for name, ch := range existing.Channels { 454 replacement, err := querier.GetBundleThatReplaces(context.TODO(), ch.Head.CsvName, pkg, name) 455 if err != nil && err.Error() != fmt.Errorf("no entry found for %s %s", pkg, name).Error() { 456 errs = append(errs, err) 457 continue 458 } 459 460 if replacement != nil { 461 continue 462 } 463 464 request := lregistry.AddToRegistryRequest{ 465 Permissive: false, 466 SkipTLSVerify: *skipTLSForRegistry, 467 PlainHTTP: *useHTTPforRegistry, 468 InputDatabase: dbFile, 469 Bundles: []string{ch.Head.BundlePath}, 470 Mode: registry.ReplacesMode, 471 ContainerTool: tool, 472 Overwrite: true, 473 } 474 475 err = adder.AddToRegistry(request) 476 if err != nil { 477 errs = append(errs, fmt.Errorf("Error overwriting bundles for package %s: %s", pkg, err)) 478 } 479 } 480 481 overwritten, err := graphLoader.Generate(pkg) 482 Expect(err).NotTo(HaveOccurred(), "Error generating graph for package %s", pkg) 483 484 if !reflect.DeepEqual(existing, overwritten) { 485 errs = append(errs, fmt.Errorf("the update graph has changed during overwrite-latest for package: %s\nWas\n%s\nIs now\n%s", pkg, existing, overwritten)) 486 } 487 } 488 489 Expect(errs).To(BeEmpty(), fmt.Sprintf("%s", errs)) 490 }) 491 } 492 493 Context("using docker", func() { 494 cmd := exec.Command("docker") 495 cmd.Stderr = os.Stderr 496 if err := cmd.Run(); err != nil { 497 GinkgoT().Logf("container tool docker not found - skipping docker-based opm e2e tests: %v", err) 498 return 499 } 500 IncludeSharedSpecs("docker") 501 }) 502 503 Context("using podman", func() { 504 cmd := exec.Command("podman", "info") 505 cmd.Stderr = os.Stderr 506 if err := cmd.Run(); err != nil { 507 GinkgoT().Logf("container tool podman not found - skipping podman-based opm e2e tests: %v", err) 508 return 509 } 510 IncludeSharedSpecs("podman") 511 }) 512 })