Skip to content

Commit a04dc1b

Browse files
kevinrizzagallettilance
authored andcommitted
Adding opm registry commands to add and rm bundles
1 parent cdfd65b commit a04dc1b

File tree

13 files changed

+1155
-68
lines changed

13 files changed

+1155
-68
lines changed

cmd/opm/registry/add.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package registry
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"fmt"
7+
"strings"
8+
9+
"github.com/sirupsen/logrus"
10+
"github.com/spf13/cobra"
11+
12+
"github.com/operator-framework/operator-registry/pkg/sqlite"
13+
)
14+
15+
func newRegistryAddCmd() *cobra.Command {
16+
rootCmd := &cobra.Command{
17+
Use: "add",
18+
Short: "add operator bundle to operator registry DB",
19+
Long: `add operator bundle to operator registry DB`,
20+
21+
PreRunE: func(cmd *cobra.Command, args []string) error {
22+
if debug, _ := cmd.Flags().GetBool("debug"); debug {
23+
logrus.SetLevel(logrus.DebugLevel)
24+
}
25+
return nil
26+
},
27+
28+
RunE: add,
29+
}
30+
31+
rootCmd.Flags().Bool("debug", false, "enable debug logging")
32+
rootCmd.Flags().StringP("database", "d", "bundles.db", "relative path to database file")
33+
// rootCmd.Flags().StringP("download-folder", "f", "downloaded", "directory where downloaded operator bundle(s) will be stored to be processed further")
34+
rootCmd.Flags().StringP("bundle-images", "b", "", "comma separated list of links to bundle image")
35+
rootCmd.Flags().Bool("permissive", false, "allow registry load errors")
36+
37+
return rootCmd
38+
}
39+
40+
func add(cmd *cobra.Command, args []string) error {
41+
bundleImages, err := cmd.Flags().GetString("bundle-images")
42+
if err != nil {
43+
return err
44+
}
45+
46+
fromFilename, err := cmd.Flags().GetString("database")
47+
if err != nil {
48+
return err
49+
}
50+
permissive, err := cmd.Flags().GetBool("permissive")
51+
if err != nil {
52+
return err
53+
}
54+
55+
bundleList := strings.Split(bundleImages, ",")
56+
57+
var errs []error
58+
59+
db, err := sql.Open("sqlite3", fromFilename)
60+
if err != nil {
61+
return err
62+
}
63+
defer db.Close()
64+
65+
dbLoader, err := sqlite.NewSQLLiteLoader(db)
66+
if err != nil {
67+
return err
68+
}
69+
if err := dbLoader.Migrate(context.TODO()); err != nil {
70+
return err
71+
}
72+
73+
for _, bundleImage := range bundleList {
74+
loader := sqlite.NewSQLLoaderForImage(dbLoader, bundleImage)
75+
if err := loader.Populate(); err != nil {
76+
err = fmt.Errorf("error loading bundle from image: %s", err)
77+
if !permissive {
78+
logrus.WithError(err).Fatal("permissive mode disabled")
79+
errs = append(errs, err)
80+
}
81+
logrus.WithError(err).Warn("permissive mode enabled")
82+
}
83+
}
84+
return nil
85+
}

cmd/opm/registry/cmd.go

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ func NewOpmRegistryCmd() *cobra.Command {
2121
}
2222

2323
rootCmd.AddCommand(newRegistryServeCmd())
24+
rootCmd.AddCommand(newRegistryAddCmd())
25+
rootCmd.AddCommand(newRegistryRmCmd())
2426

2527
return rootCmd
2628
}

cmd/opm/registry/rm.go

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package registry
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"fmt"
7+
8+
"github.com/sirupsen/logrus"
9+
"github.com/spf13/cobra"
10+
11+
"github.com/operator-framework/operator-registry/pkg/sqlite"
12+
)
13+
14+
func newRegistryRmCmd() *cobra.Command {
15+
rootCmd := &cobra.Command{
16+
Use: "rm",
17+
Short: "remove operator from operator registry DB",
18+
Long: `Remove operator from operator registry DB`,
19+
20+
PreRunE: func(cmd *cobra.Command, args []string) error {
21+
if debug, _ := cmd.Flags().GetBool("debug"); debug {
22+
logrus.SetLevel(logrus.DebugLevel)
23+
}
24+
return nil
25+
},
26+
27+
RunE: rm,
28+
}
29+
30+
rootCmd.Flags().Bool("debug", false, "enable debug logging")
31+
rootCmd.Flags().StringP("database", "d", "bundles.db", "relative path to database file")
32+
rootCmd.Flags().StringP("packages", "o", "", "comma separated list of package names to be deleted")
33+
rootCmd.Flags().Bool("permissive", false, "allow registry load errors")
34+
35+
return rootCmd
36+
}
37+
38+
func rm(cmd *cobra.Command, args []string) error {
39+
fromFilename, err := cmd.Flags().GetString("database")
40+
if err != nil {
41+
return err
42+
}
43+
packages, err := cmd.Flags().GetString("packages")
44+
if err != nil {
45+
return err
46+
}
47+
permissive, err := cmd.Flags().GetBool("permissive")
48+
if err != nil {
49+
return err
50+
}
51+
52+
db, err := sql.Open("sqlite3", fromFilename)
53+
if err != nil {
54+
return err
55+
}
56+
defer db.Close()
57+
58+
dbLoader, err := sqlite.NewSQLLiteLoader(db)
59+
if err != nil {
60+
return err
61+
}
62+
if err := dbLoader.Migrate(context.TODO()); err != nil {
63+
return err
64+
}
65+
66+
remover := sqlite.NewSQLRemoverForPackages(dbLoader, packages)
67+
if err := remover.Remove(); err != nil {
68+
err = fmt.Errorf("error deleting packages from database: %s", err)
69+
if !permissive {
70+
logrus.WithError(err).Fatal("permissive mode disabled")
71+
return err
72+
}
73+
logrus.WithError(err).Warn("permissive mode enabled")
74+
}
75+
76+
return nil
77+
}

cmd/opm/registry/serve.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func newRegistryServeCmd() *cobra.Command {
3131
return nil
3232
},
3333

34-
RunE: runRegistryServeCmdFunc,
34+
RunE: serve,
3535
}
3636

3737
rootCmd.Flags().Bool("debug", false, "enable debug logging")
@@ -43,7 +43,7 @@ func newRegistryServeCmd() *cobra.Command {
4343

4444
}
4545

46-
func runRegistryServeCmdFunc(cmd *cobra.Command, args []string) error {
46+
func serve(cmd *cobra.Command, args []string) error {
4747
// Immediately set up termination log
4848
terminationLogPath, err := cmd.Flags().GetString("termination-log")
4949
if err != nil {

pkg/containertools/bundlereader.go

+185
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package containertools
2+
3+
import (
4+
"archive/tar"
5+
"bytes"
6+
"encoding/json"
7+
"fmt"
8+
"io"
9+
"io/ioutil"
10+
"os"
11+
"path/filepath"
12+
)
13+
14+
const (
15+
imageManifestName = "manifest.json"
16+
)
17+
18+
// imageManifest is the object format of container image manifest files
19+
// use this type to parse manifest.json files inside container image blobs
20+
type imageManifest struct {
21+
Layers []string `json:”Layers”`
22+
}
23+
24+
type BundleReader struct {
25+
}
26+
27+
func NewBundleReader() *BundleReader {
28+
return &BundleReader{}
29+
}
30+
31+
func (b *BundleReader) GetBundle(image, outputDir string) error {
32+
r := NewCommandRunner(Podman)
33+
34+
// Create the output directory if it doesn't exist
35+
if _, err := os.Stat(outputDir); os.IsNotExist(err) {
36+
os.Mkdir(outputDir, 0777)
37+
}
38+
39+
err := r.Pull(image)
40+
if err != nil {
41+
return err
42+
}
43+
44+
workingDir, err := ioutil.TempDir("./", "bundle_staging_")
45+
if err != nil {
46+
return err
47+
}
48+
defer os.RemoveAll(workingDir)
49+
50+
//TODO: Use filepath package here
51+
rootTarfile := filepath.Join(workingDir, "bundle.tar")
52+
53+
err = r.Save(image, rootTarfile)
54+
if err != nil {
55+
return err
56+
}
57+
58+
f, err := os.Open(rootTarfile)
59+
if err != nil {
60+
return err
61+
}
62+
defer f.Close()
63+
64+
// Read the manifest.json file to find the right embedded tarball
65+
layerTarball, err := getManifestLayer(tar.NewReader(f))
66+
if err != nil {
67+
return err
68+
}
69+
70+
f, err = os.Open(rootTarfile)
71+
if err != nil {
72+
return err
73+
}
74+
defer f.Close()
75+
76+
// Untar the top image layer tarball and push the bundle manifests to the output directory
77+
err = extractBundleManifests(layerTarball, outputDir, tar.NewReader(f))
78+
if err != nil {
79+
return err
80+
}
81+
82+
return nil
83+
}
84+
85+
func getManifestLayer(tarReader *tar.Reader) (string, error) {
86+
for {
87+
header, err := tarReader.Next()
88+
if err != nil {
89+
if err == io.EOF {
90+
return "", fmt.Errorf("invalid bundle image: unable to find manifest.json")
91+
}
92+
return "", err
93+
}
94+
95+
if header.Name == imageManifestName {
96+
buf := new(bytes.Buffer)
97+
buf.ReadFrom(tarReader)
98+
b := buf.Bytes()
99+
100+
manifests := make([]imageManifest, 0)
101+
err := json.Unmarshal(b, &manifests)
102+
if err != nil {
103+
return "", err
104+
}
105+
106+
if len(manifests) == 0 {
107+
return "", fmt.Errorf("invalid bundle image: manifest.json missing manifest data")
108+
}
109+
110+
topManifest := manifests[0]
111+
112+
if len(topManifest.Layers) == 0 {
113+
return "", fmt.Errorf("invalid bundle image: manifest has no layers")
114+
}
115+
116+
return topManifest.Layers[0], nil
117+
}
118+
}
119+
}
120+
121+
func extractBundleManifests(layerTarball, outputDir string, tarReader *tar.Reader) error {
122+
for {
123+
header, err := tarReader.Next()
124+
if err != nil {
125+
if err == io.EOF {
126+
return fmt.Errorf("Manifest error: Layer tarball does not exist in bundle")
127+
}
128+
return err
129+
}
130+
131+
if header.Typeflag == tar.TypeReg {
132+
if header.Name == layerTarball {
133+
// Found the embedded top layer tarball
134+
layerReader := tar.NewReader(tarReader)
135+
136+
err = extractTarballToDir(outputDir, layerReader)
137+
if err != nil {
138+
return err
139+
}
140+
}
141+
142+
continue
143+
} else {
144+
return nil
145+
}
146+
}
147+
}
148+
149+
func extractTarballToDir(outputDir string, tarReader *tar.Reader) error {
150+
for {
151+
header, err := tarReader.Next()
152+
if err != nil {
153+
if err == io.EOF {
154+
return nil
155+
}
156+
return err
157+
}
158+
159+
switch header.Typeflag {
160+
case tar.TypeDir:
161+
// Create the directory if it doesn't exist
162+
directoryToWrite := filepath.Join(outputDir, header.Name)
163+
if _, err := os.Stat(directoryToWrite); os.IsNotExist(err) {
164+
os.Mkdir(directoryToWrite, 0777)
165+
}
166+
case tar.TypeReg:
167+
buf := new(bytes.Buffer)
168+
buf.ReadFrom(tarReader)
169+
b := buf.Bytes()
170+
171+
manifestToWrite := filepath.Join(outputDir, header.Name)
172+
173+
m, err := os.Create(manifestToWrite)
174+
if err != nil {
175+
return err
176+
}
177+
defer m.Close()
178+
179+
_, err = m.Write(b)
180+
if err != nil {
181+
return err
182+
}
183+
}
184+
}
185+
}

0 commit comments

Comments
 (0)