|
1 | 1 | package indexer
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "context" |
| 5 | + "database/sql" |
| 6 | + "fmt" |
4 | 7 | "io"
|
5 | 8 | "io/ioutil"
|
6 | 9 | "os"
|
7 | 10 | "path"
|
| 11 | + "path/filepath" |
8 | 12 |
|
9 | 13 | "github.com/operator-framework/operator-registry/pkg/containertools"
|
| 14 | + "github.com/operator-framework/operator-registry/pkg/lib/bundle" |
10 | 15 | "github.com/operator-framework/operator-registry/pkg/lib/registry"
|
| 16 | + pregistry "github.com/operator-framework/operator-registry/pkg/registry" |
| 17 | + "github.com/operator-framework/operator-registry/pkg/sqlite" |
11 | 18 |
|
12 | 19 | "github.com/sirupsen/logrus"
|
| 20 | + "gopkg.in/yaml.v2" |
| 21 | + utilerrors "k8s.io/apimachinery/pkg/util/errors" |
13 | 22 | )
|
14 | 23 |
|
15 | 24 | const (
|
16 | 25 | defaultDockerfileName = "index.Dockerfile"
|
17 |
| - defaultImageTag = "operator-registry-index:latest" |
| 26 | + defaultImageTag = "operator-registry-index:latest" |
18 | 27 | defaultDatabaseFolder = "database"
|
19 |
| - defaultDatabaseFile = "index.db" |
| 28 | + defaultDatabaseFile = "index.db" |
20 | 29 | )
|
21 | 30 |
|
22 | 31 | // ImageIndexer is a struct implementation of the Indexer interface
|
23 | 32 | type ImageIndexer struct {
|
24 | 33 | DockerfileGenerator containertools.DockerfileGenerator
|
25 |
| - CommandRunner containertools.CommandRunner |
26 |
| - LabelReader containertools.LabelReader |
27 |
| - ImageReader containertools.ImageReader |
28 |
| - RegistryAdder registry.RegistryAdder |
29 |
| - RegistryDeleter registry.RegistryDeleter |
30 |
| - ContainerTool string |
31 |
| - Logger *logrus.Entry |
| 34 | + CommandRunner containertools.CommandRunner |
| 35 | + LabelReader containertools.LabelReader |
| 36 | + ImageReader containertools.ImageReader |
| 37 | + RegistryAdder registry.RegistryAdder |
| 38 | + RegistryDeleter registry.RegistryDeleter |
| 39 | + ContainerTool string |
| 40 | + Logger *logrus.Entry |
32 | 41 | }
|
33 | 42 |
|
34 | 43 | // AddToIndexRequest defines the parameters to send to the AddToIndex API
|
35 | 44 | type AddToIndexRequest struct {
|
36 |
| - Generate bool |
37 |
| - Permissive bool |
| 45 | + Generate bool |
| 46 | + Permissive bool |
38 | 47 | BinarySourceImage string
|
39 |
| - FromIndex string |
40 |
| - OutDockerfile string |
41 |
| - Bundles []string |
42 |
| - Tag string |
| 48 | + FromIndex string |
| 49 | + OutDockerfile string |
| 50 | + Bundles []string |
| 51 | + Tag string |
43 | 52 | }
|
44 | 53 |
|
45 | 54 | // AddToIndex is an aggregate API used to generate a registry index image with additional bundles
|
@@ -77,9 +86,9 @@ func (i ImageIndexer) AddToIndex(request AddToIndexRequest) error {
|
77 | 86 |
|
78 | 87 | // Run opm registry add on the database
|
79 | 88 | addToRegistryReq := registry.AddToRegistryRequest{
|
80 |
| - Bundles: request.Bundles, |
| 89 | + Bundles: request.Bundles, |
81 | 90 | InputDatabase: databaseFile,
|
82 |
| - Permissive: request.Permissive, |
| 91 | + Permissive: request.Permissive, |
83 | 92 | ContainerTool: i.ContainerTool,
|
84 | 93 | }
|
85 | 94 |
|
@@ -107,16 +116,16 @@ func (i ImageIndexer) AddToIndex(request AddToIndexRequest) error {
|
107 | 116 |
|
108 | 117 | // DeleteFromIndexRequest defines the parameters to send to the DeleteFromIndex API
|
109 | 118 | type DeleteFromIndexRequest struct {
|
110 |
| - Generate bool |
111 |
| - Permissive bool |
| 119 | + Generate bool |
| 120 | + Permissive bool |
112 | 121 | BinarySourceImage string
|
113 |
| - FromIndex string |
114 |
| - OutDockerfile string |
115 |
| - Tag string |
116 |
| - Operators []string |
| 122 | + FromIndex string |
| 123 | + OutDockerfile string |
| 124 | + Tag string |
| 125 | + Operators []string |
117 | 126 | }
|
118 | 127 |
|
119 |
| -// DeleteFromIndex is an aggregate API used to generate a registry index image |
| 128 | +// DeleteFromIndex is an aggregate API used to generate a registry index image |
120 | 129 | // without specific operators
|
121 | 130 | func (i ImageIndexer) DeleteFromIndex(request DeleteFromIndexRequest) error {
|
122 | 131 | databaseFile := defaultDatabaseFile
|
@@ -154,9 +163,9 @@ func (i ImageIndexer) DeleteFromIndex(request DeleteFromIndexRequest) error {
|
154 | 163 |
|
155 | 164 | // Run opm registry add on the database
|
156 | 165 | deleteFromRegistryReq := registry.DeleteFromRegistryRequest{
|
157 |
| - Packages: request.Operators, |
| 166 | + Packages: request.Operators, |
158 | 167 | InputDatabase: databaseFile,
|
159 |
| - Permissive: request.Permissive, |
| 168 | + Permissive: request.Permissive, |
160 | 169 | }
|
161 | 170 |
|
162 | 171 | // Add the bundles to the registry
|
@@ -283,3 +292,162 @@ func write(dockerfileText, outDockerfile string, logger *logrus.Entry) error {
|
283 | 292 |
|
284 | 293 | return nil
|
285 | 294 | }
|
| 295 | + |
| 296 | +// ExportFromIndexRequest defines the parameters to send to the ExportFromIndex API |
| 297 | +type ExportFromIndexRequest struct { |
| 298 | + Index string |
| 299 | + Package string |
| 300 | + DownloadPath string |
| 301 | + ContainerTool string |
| 302 | +} |
| 303 | + |
| 304 | +// ExportFromIndex is an aggregate API used to specify operators from |
| 305 | +// an index image |
| 306 | +func (i ImageIndexer) ExportFromIndex(request ExportFromIndexRequest) error { |
| 307 | + databaseFile := defaultDatabaseFile |
| 308 | + |
| 309 | + // set a temp directory |
| 310 | + workingDir, err := ioutil.TempDir("./", "index_tmp") |
| 311 | + if err != nil { |
| 312 | + return err |
| 313 | + } |
| 314 | + defer os.RemoveAll(workingDir) |
| 315 | + |
| 316 | + db, err := sql.Open("sqlite3", databaseFile) |
| 317 | + if err != nil { |
| 318 | + return err |
| 319 | + } |
| 320 | + defer db.Close() |
| 321 | + |
| 322 | + dbQuerier := sqlite.NewSQLLiteQuerierFromDb(db) |
| 323 | + if err != nil { |
| 324 | + return err |
| 325 | + } |
| 326 | + |
| 327 | + // extract the index database to the file |
| 328 | + databaseFile, err = i.WriteIndexDBFile(request.Index, workingDir, databaseFile) |
| 329 | + if err != nil { |
| 330 | + return err |
| 331 | + } |
| 332 | + |
| 333 | + bundles, err := getBundlesToExport(dbQuerier, request.Package) |
| 334 | + if err != nil { |
| 335 | + return err |
| 336 | + } |
| 337 | + i.Logger.Infof("Preparing to pull bundles %+q", bundles) |
| 338 | + |
| 339 | + // Creating downloadPath dir |
| 340 | + if err := os.MkdirAll(request.DownloadPath, 0777); err != nil { |
| 341 | + return err |
| 342 | + } |
| 343 | + |
| 344 | + var errs []error |
| 345 | + for _, bundleImage := range bundles { |
| 346 | + // try to name the folder |
| 347 | + folderName, err := dbQuerier.GetBundleVersion(context.TODO(), bundleImage) |
| 348 | + if err != nil { |
| 349 | + return err |
| 350 | + } |
| 351 | + if folderName == "" { |
| 352 | + // operator-registry does not care about the folder name |
| 353 | + folderName = bundleImage |
| 354 | + } |
| 355 | + exporter := bundle.NewSQLExporterForBundle(bundleImage, filepath.Join(request.DownloadPath, folderName), request.ContainerTool) |
| 356 | + if err := exporter.Export(); err != nil { |
| 357 | + err = fmt.Errorf("error exporting bundle from image: %s", err) |
| 358 | + errs = append(errs, err) |
| 359 | + } |
| 360 | + } |
| 361 | + if err != nil { |
| 362 | + errs = append(errs, err) |
| 363 | + return utilerrors.NewAggregate(errs) |
| 364 | + } |
| 365 | + |
| 366 | + err = generatePackageYaml(dbQuerier, request.Package, request.DownloadPath) |
| 367 | + if err != nil { |
| 368 | + errs = append(errs, err) |
| 369 | + } |
| 370 | + return utilerrors.NewAggregate(errs) |
| 371 | +} |
| 372 | + |
| 373 | +func (i ImageIndexer) WriteIndexDBFile(index, workingDir, databaseFile string) (string, error) { |
| 374 | + if index != "" { |
| 375 | + i.Logger.Infof("Pulling previous image %s to get metadata", index) |
| 376 | + |
| 377 | + // Get the old index image's dbLocationLabel to find this path |
| 378 | + labels, err := i.LabelReader.GetLabelsFromImage(index) |
| 379 | + if err != nil { |
| 380 | + return "", err |
| 381 | + } |
| 382 | + if dbLocation, ok := labels[containertools.DbLocationLabel]; ok { |
| 383 | + i.Logger.Infof("Previous db location %s", dbLocation) |
| 384 | + |
| 385 | + // extract the database to the file |
| 386 | + err = i.ImageReader.GetImageData(index, workingDir) |
| 387 | + if err != nil { |
| 388 | + return "", err |
| 389 | + } |
| 390 | + |
| 391 | + databaseFile = path.Join(workingDir, dbLocation) |
| 392 | + } |
| 393 | + } else { |
| 394 | + databaseFile = path.Join(workingDir, databaseFile) |
| 395 | + } |
| 396 | + return databaseFile, nil |
| 397 | +} |
| 398 | + |
| 399 | +func getBundlesToExport(dbQuerier pregistry.Query, packageName string) ([]string, error) { |
| 400 | + bundles, err := dbQuerier.GetBundlePathsForPackage(context.TODO(), packageName) |
| 401 | + if err != nil { |
| 402 | + return nil, err |
| 403 | + } |
| 404 | + return bundles, nil |
| 405 | +} |
| 406 | + |
| 407 | +func generatePackageYaml(dbQuerier pregistry.Query, packageName, downloadPath string) error { |
| 408 | + var errs []error |
| 409 | + |
| 410 | + defaultChannel, err := dbQuerier.GetDefaultChannelForPackage(context.TODO(), packageName) |
| 411 | + if err != nil { |
| 412 | + return err |
| 413 | + } |
| 414 | + |
| 415 | + channelList, err := dbQuerier.ListChannels(context.TODO(), packageName) |
| 416 | + if err != nil { |
| 417 | + return err |
| 418 | + } |
| 419 | + |
| 420 | + channels := []pregistry.PackageChannel{} |
| 421 | + for _, ch := range channelList { |
| 422 | + csvName, err := dbQuerier.GetCurrentCSVNameForChannel(context.TODO(), packageName, ch) |
| 423 | + if err != nil { |
| 424 | + err = fmt.Errorf("error exporting bundle from image: %s", err) |
| 425 | + errs = append(errs, err) |
| 426 | + continue |
| 427 | + } |
| 428 | + channels = append(channels, |
| 429 | + pregistry.PackageChannel{ |
| 430 | + Name: ch, |
| 431 | + CurrentCSVName: csvName, |
| 432 | + }) |
| 433 | + } |
| 434 | + |
| 435 | + manifest := pregistry.PackageManifest{ |
| 436 | + PackageName: packageName, |
| 437 | + DefaultChannelName: defaultChannel, |
| 438 | + Channels: channels, |
| 439 | + } |
| 440 | + |
| 441 | + manifestBytes, err := yaml.Marshal(&manifest) |
| 442 | + if err != nil { |
| 443 | + errs = append(errs, err) |
| 444 | + return utilerrors.NewAggregate(errs) |
| 445 | + } |
| 446 | + |
| 447 | + err = bundle.WriteFile("package.yaml", downloadPath, manifestBytes) |
| 448 | + if err != nil { |
| 449 | + errs = append(errs, err) |
| 450 | + } |
| 451 | + |
| 452 | + return utilerrors.NewAggregate(errs) |
| 453 | +} |
0 commit comments