Skip to content

Commit a022e61

Browse files
committedApr 17, 2020
feat(opm): add unprivileged index extract
index extract, by default or with the "none" containertool option, will pull and extract indexes and bundles without a deamon
1 parent 6acbbca commit a022e61

File tree

7 files changed

+141
-48
lines changed

7 files changed

+141
-48
lines changed
 

‎cmd/opm/index/export.go

+3-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package index
22

33
import (
4-
"fmt"
54
"github.com/sirupsen/logrus"
65
"github.com/spf13/cobra"
76
"k8s.io/kubectl/pkg/util/templates"
@@ -46,7 +45,7 @@ func newIndexExportCmd() *cobra.Command {
4645
logrus.Panic("Failed to set required `package` flag for `index export`")
4746
}
4847
indexCmd.Flags().StringP("download-folder", "f", "downloaded", "directory where downloaded operator bundle(s) will be stored")
49-
indexCmd.Flags().StringP("container-tool", "c", "podman", "tool to interact with container images (save, build, etc.). One of: [none, docker, podman]")
48+
indexCmd.Flags().StringP("container-tool", "c", "none", "tool to interact with container images (save, build, etc.). One of: [none, docker, podman]")
5049
if err := indexCmd.Flags().MarkHidden("debug"); err != nil {
5150
logrus.Panic(err.Error())
5251
}
@@ -76,21 +75,17 @@ func runIndexExportCmdFunc(cmd *cobra.Command, args []string) error {
7675
return err
7776
}
7877

79-
if containerTool == "none" {
80-
return fmt.Errorf("none is not a valid container-tool for index add")
81-
}
82-
8378
logger := logrus.WithFields(logrus.Fields{"index": index, "package": packageName})
8479

8580
logger.Info("export from the index")
8681

87-
indexExporter := indexer.NewIndexExporter(containertools.NewContainerTool(containerTool, containertools.PodmanTool), logger)
82+
indexExporter := indexer.NewIndexExporter(containertools.NewContainerTool(containerTool, containertools.NoneTool), logger)
8883

8984
request := indexer.ExportFromIndexRequest{
9085
Index: index,
9186
Package: packageName,
9287
DownloadPath: downloadPath,
93-
ContainerTool: containerTool,
88+
ContainerTool: containertools.NewContainerTool(containerTool, containertools.NoneTool),
9489
}
9590

9691
err = indexExporter.ExportFromIndex(request)

‎pkg/image/containerdregistry/registry.go

+49
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package containerdregistry
22

33
import (
44
"archive/tar"
5+
"bytes"
56
"context"
7+
"encoding/json"
68
"fmt"
79
"github.com/containerd/containerd/archive"
810
"github.com/containerd/containerd/archive/compression"
@@ -14,6 +16,7 @@ import (
1416
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
1517
"github.com/sirupsen/logrus"
1618
"io"
19+
"io/ioutil"
1720
"os"
1821

1922
"github.com/operator-framework/operator-registry/pkg/image"
@@ -93,6 +96,52 @@ func (r *Registry) Unpack(ctx context.Context, ref image.Reference, dir string)
9396
return nil
9497
}
9598

99+
// Labels gets the labels for an image reference.
100+
func (r *Registry) Labels(ctx context.Context, ref image.Reference) (map[string]string, error) {
101+
// Set the default namespace if unset
102+
ctx = ensureNamespace(ctx)
103+
tmpDir, err := ioutil.TempDir("./", "bundle_tmp")
104+
if err != nil {
105+
return nil, err
106+
}
107+
defer os.RemoveAll(tmpDir)
108+
109+
img, err := r.Images().Get(ctx, ref.String())
110+
if err != nil {
111+
return nil, err
112+
}
113+
114+
manifest, err := images.Manifest(ctx, r.Content(), img.Target, r.platform)
115+
if err != nil {
116+
return nil, err
117+
}
118+
119+
ra, err := r.Content().ReaderAt(ctx, manifest.Config)
120+
if err != nil {
121+
return nil, err
122+
}
123+
defer ra.Close()
124+
125+
decompressed, err := compression.DecompressStream(io.NewSectionReader(ra, 0, ra.Size()))
126+
if err != nil {
127+
return nil, err
128+
}
129+
var buf bytes.Buffer
130+
if _, err := io.Copy(&buf, decompressed); err != nil {
131+
return nil, err
132+
}
133+
r.log.Warn(buf.String())
134+
135+
var imageConfig ocispec.Image
136+
137+
if err := json.Unmarshal(buf.Bytes(), &imageConfig); err != nil {
138+
return nil, err
139+
}
140+
141+
return imageConfig.Config.Labels, nil
142+
}
143+
144+
96145
// Destroy cleans up the on-disk boltdb file and other cache files, unless preserve cache is true
97146
func (r *Registry) Destroy() (err error) {
98147
return r.destroy()

‎pkg/image/execregistry/registry.go

+8
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ func (r *Registry) Unpack(ctx context.Context, ref image.Reference, dir string)
3838
}.GetImageData(ref.String(), dir)
3939
}
4040

41+
// Labels gets the labels for an image reference.
42+
func (r *Registry) Labels(ctx context.Context, ref image.Reference) (map[string]string, error) {
43+
return containertools.ImageLabelReader{
44+
Cmd: r.cmd,
45+
Logger: r.log,
46+
}.GetLabelsFromImage(ref.String())
47+
}
48+
4149
// Destroy is no-op for exec tools
4250
func (r *Registry) Destroy() error {
4351
return nil

‎pkg/image/registry.go

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ type Registry interface {
1818
// If the referenced image does not exist in the registry, an error is returned.
1919
Unpack(ctx context.Context, ref Reference, dir string) error
2020

21+
// Labels gets the labels for an image that is already stored.
22+
Labels(ctx context.Context, ref Reference) (map[string]string, error)
23+
2124
// Destroy cleans up any on-disk resources used to track images
2225
Destroy() error
2326

@@ -26,3 +29,4 @@ type Registry interface {
2629
// If it exists, it's used as the base image.
2730
// Pack(ctx context.Context, ref Reference, from io.Reader) (next string, err error)
2831
}
32+

‎pkg/lib/bundle/exporter.go

+30-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package bundle
22

33
import (
4+
"context"
5+
"github.com/operator-framework/operator-registry/pkg/image"
6+
"github.com/operator-framework/operator-registry/pkg/image/execregistry"
47
"io/ioutil"
58
"os"
69
"path/filepath"
@@ -9,6 +12,7 @@ import (
912
"github.com/sirupsen/logrus"
1013

1114
"github.com/operator-framework/operator-registry/pkg/containertools"
15+
"github.com/operator-framework/operator-registry/pkg/image/containerdregistry"
1216
)
1317

1418
// BundleExporter exports the manifests of a bundle image into a directory
@@ -18,11 +22,11 @@ type BundleExporter struct {
1822
containerTool containertools.ContainerTool
1923
}
2024

21-
func NewSQLExporterForBundle(image, directory, containerTool string) *BundleExporter {
25+
func NewSQLExporterForBundle(image, directory string, containerTool containertools.ContainerTool) *BundleExporter {
2226
return &BundleExporter{
2327
image: image,
2428
directory: directory,
25-
containerTool: containertools.NewContainerTool(containerTool, containertools.NoneTool),
29+
containerTool: containerTool,
2630
}
2731
}
2832

@@ -36,13 +40,33 @@ func (i *BundleExporter) Export() error {
3640
}
3741
defer os.RemoveAll(tmpDir)
3842

39-
// Pull the image and get the manifests
40-
reader := containertools.NewImageReader(i.containerTool, log)
43+
var reg image.Registry
44+
var rerr error
45+
switch i.containerTool {
46+
case containertools.NoneTool:
47+
reg, rerr = containerdregistry.NewRegistry(containerdregistry.WithLog(log))
48+
case containertools.PodmanTool:
49+
fallthrough
50+
case containertools.DockerTool:
51+
reg, rerr = execregistry.NewRegistry(i.containerTool, log)
52+
}
53+
if rerr != nil {
54+
return rerr
55+
}
56+
defer func() {
57+
if err := reg.Destroy(); err != nil {
58+
log.WithError(err).Warn("error destroying local cache")
59+
}
60+
}()
4161

42-
err = reader.GetImageData(i.image, tmpDir)
43-
if err != nil {
62+
if err := reg.Pull(context.TODO(), image.SimpleReference(i.image)); err != nil {
4463
return err
4564
}
65+
66+
if err := reg.Unpack(context.TODO(), image.SimpleReference(i.image), tmpDir); err != nil {
67+
return err
68+
}
69+
4670
if err := os.MkdirAll(i.directory, 0777); err != nil {
4771
return err
4872
}

‎pkg/lib/indexer/indexer.go

+34-33
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import (
44
"context"
55
"database/sql"
66
"fmt"
7+
"github.com/operator-framework/operator-registry/pkg/image"
8+
"github.com/operator-framework/operator-registry/pkg/image/containerdregistry"
9+
"github.com/operator-framework/operator-registry/pkg/image/execregistry"
710
"io"
811
"io/ioutil"
912
"os"
@@ -210,19 +213,43 @@ func (i ImageIndexer) getDatabaseFile(workingDir, fromIndex string) (string, err
210213
// Pull the fromIndex
211214
i.Logger.Infof("Pulling previous image %s to get metadata", fromIndex)
212215

216+
var reg image.Registry
217+
var rerr error
218+
switch i.ContainerTool {
219+
case containertools.NoneTool:
220+
reg, rerr = containerdregistry.NewRegistry(containerdregistry.WithLog(i.Logger))
221+
case containertools.PodmanTool:
222+
fallthrough
223+
case containertools.DockerTool:
224+
reg, rerr = execregistry.NewRegistry(i.ContainerTool, i.Logger)
225+
}
226+
if rerr != nil {
227+
return "", rerr
228+
}
229+
defer func() {
230+
if err := reg.Destroy(); err != nil {
231+
i.Logger.WithError(err).Warn("error destroying local cache")
232+
}
233+
}()
234+
235+
imageRef := image.SimpleReference(fromIndex)
236+
237+
if err := reg.Pull(context.TODO(), imageRef); err != nil {
238+
return "", err
239+
}
240+
213241
// Get the old index image's dbLocationLabel to find this path
214-
labels, err := i.LabelReader.GetLabelsFromImage(fromIndex)
242+
labels, err := reg.Labels(context.TODO(), imageRef)
215243
if err != nil {
216244
return "", err
217245
}
246+
218247
dbLocation, ok := labels[containertools.DbLocationLabel]
219248
if !ok {
220-
return "", fmt.Errorf("Index image %s missing label %s", fromIndex, containertools.DbLocationLabel)
249+
return "", fmt.Errorf("index image %s missing label %s", fromIndex, containertools.DbLocationLabel)
221250
}
222251

223-
// extract the database to the working directory
224-
err = i.ImageReader.GetImageData(fromIndex, workingDir)
225-
if err != nil {
252+
if err := reg.Unpack(context.TODO(), imageRef, workingDir); err != nil {
226253
return "", err
227254
}
228255

@@ -341,7 +368,7 @@ type ExportFromIndexRequest struct {
341368
Index string
342369
Package string
343370
DownloadPath string
344-
ContainerTool string
371+
ContainerTool containertools.ContainerTool
345372
}
346373

347374
// ExportFromIndex is an aggregate API used to specify operators from
@@ -355,7 +382,7 @@ func (i ImageIndexer) ExportFromIndex(request ExportFromIndexRequest) error {
355382
defer os.RemoveAll(workingDir)
356383

357384
// extract the index database to the file
358-
databaseFile, err := i.WriteIndexDBFile(request.Index, workingDir, defaultDatabaseFile)
385+
databaseFile, err := i.getDatabaseFile(workingDir, request.Index)
359386
if err != nil {
360387
return err
361388
}
@@ -411,32 +438,6 @@ func (i ImageIndexer) ExportFromIndex(request ExportFromIndexRequest) error {
411438
return utilerrors.NewAggregate(errs)
412439
}
413440

414-
func (i ImageIndexer) WriteIndexDBFile(index, workingDir, databaseFile string) (string, error) {
415-
if index != "" {
416-
i.Logger.Infof("Pulling previous image %s to get metadata", index)
417-
418-
// Get the old index image's dbLocationLabel to find this path
419-
labels, err := i.LabelReader.GetLabelsFromImage(index)
420-
if err != nil {
421-
return "", err
422-
}
423-
if dbLocation, ok := labels[containertools.DbLocationLabel]; ok {
424-
i.Logger.Infof("Previous db location %s", dbLocation)
425-
426-
// extract the database to the file
427-
err = i.ImageReader.GetImageData(index, workingDir)
428-
if err != nil {
429-
return "", err
430-
}
431-
432-
databaseFile = path.Join(workingDir, dbLocation)
433-
}
434-
} else {
435-
databaseFile = path.Join(workingDir, databaseFile)
436-
}
437-
return databaseFile, nil
438-
}
439-
440441
func getBundlesToExport(dbQuerier pregistry.Query, packageName string) ([]string, error) {
441442
bundles, err := dbQuerier.GetBundlePathsForPackage(context.TODO(), packageName)
442443
if err != nil {

‎test/e2e/opm_test.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func exportWith(containerTool string) error {
148148
Index: indexImage2,
149149
Package: packageName,
150150
DownloadPath: "downloaded",
151-
ContainerTool: containerTool,
151+
ContainerTool: containertools.NewContainerTool(containerTool, containertools.NoneTool),
152152
}
153153

154154
return indexExporter.ExportFromIndex(request)
@@ -223,6 +223,18 @@ var _ = Describe("opm", func() {
223223
By("loading manifests from a directory")
224224
err = initialize()
225225
Expect(err).NotTo(HaveOccurred())
226+
227+
// clean and try again with containerd
228+
err = os.RemoveAll("downloaded")
229+
Expect(err).NotTo(HaveOccurred())
230+
231+
By("exporting an index to disk with containerd")
232+
err = exportWith(containertools.NoneTool.String())
233+
Expect(err).NotTo(HaveOccurred())
234+
235+
By("loading manifests from a containerd-extracted directory")
236+
err = initialize()
237+
Expect(err).NotTo(HaveOccurred())
226238
})
227239
}
228240

0 commit comments

Comments
 (0)
Please sign in to comment.