Skip to content

Commit 018a040

Browse files
authoredAug 7, 2020
Merge pull request #413 from kevinrizza/cleanup-command
Bug 1866930: Clean up stranded bundles
2 parents 944be59 + e144a57 commit 018a040

33 files changed

+18360
-13
lines changed
 

‎cmd/opm/index/cmd.go

+1
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ func AddCommand(parent *cobra.Command) {
2626
cmd.AddCommand(newIndexExportCmd())
2727
cmd.AddCommand(newIndexPruneCmd())
2828
cmd.AddCommand(newIndexDeprecateTruncateCmd())
29+
cmd.AddCommand(newIndexPruneStrandedCmd())
2930
}

‎cmd/opm/index/prunestranded.go

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package index
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/sirupsen/logrus"
7+
"github.com/spf13/cobra"
8+
9+
"github.com/operator-framework/operator-registry/pkg/containertools"
10+
"github.com/operator-framework/operator-registry/pkg/lib/indexer"
11+
)
12+
13+
func newIndexPruneStrandedCmd() *cobra.Command {
14+
indexCmd := &cobra.Command{
15+
Use: "prune-stranded",
16+
Short: "prune an index of stranded bundles",
17+
Long: `prune an index of stranded bundles - bundles that are not associated with a particular package`,
18+
19+
PreRunE: func(cmd *cobra.Command, args []string) error {
20+
if debug, _ := cmd.Flags().GetBool("debug"); debug {
21+
logrus.SetLevel(logrus.DebugLevel)
22+
}
23+
return nil
24+
},
25+
26+
RunE: runIndexPruneStrandedCmdFunc,
27+
}
28+
29+
indexCmd.Flags().Bool("debug", false, "enable debug logging")
30+
indexCmd.Flags().Bool("generate", false, "if enabled, just creates the dockerfile and saves it to local disk")
31+
indexCmd.Flags().StringP("out-dockerfile", "d", "", "if generating the dockerfile, this flag is used to (optionally) specify a dockerfile name")
32+
indexCmd.Flags().StringP("from-index", "f", "", "index to prune")
33+
if err := indexCmd.MarkFlagRequired("from-index"); err != nil {
34+
logrus.Panic("Failed to set required `from-index` flag for `index prune-stranded`")
35+
}
36+
indexCmd.Flags().StringP("binary-image", "i", "", "container image for on-image `opm` command")
37+
indexCmd.Flags().StringP("container-tool", "c", "podman", "tool to interact with container images (save, build, etc.). One of: [docker, podman]")
38+
indexCmd.Flags().StringP("tag", "t", "", "custom tag for container image being built")
39+
40+
if err := indexCmd.Flags().MarkHidden("debug"); err != nil {
41+
logrus.Panic(err.Error())
42+
}
43+
44+
return indexCmd
45+
46+
}
47+
48+
func runIndexPruneStrandedCmdFunc(cmd *cobra.Command, args []string) error {
49+
generate, err := cmd.Flags().GetBool("generate")
50+
if err != nil {
51+
return err
52+
}
53+
54+
outDockerfile, err := cmd.Flags().GetString("out-dockerfile")
55+
if err != nil {
56+
return err
57+
}
58+
59+
fromIndex, err := cmd.Flags().GetString("from-index")
60+
if err != nil {
61+
return err
62+
}
63+
64+
binaryImage, err := cmd.Flags().GetString("binary-image")
65+
if err != nil {
66+
return err
67+
}
68+
69+
containerTool, err := cmd.Flags().GetString("container-tool")
70+
if err != nil {
71+
return err
72+
}
73+
74+
if containerTool == "none" {
75+
return fmt.Errorf("none is not a valid container-tool for index prune")
76+
}
77+
78+
tag, err := cmd.Flags().GetString("tag")
79+
if err != nil {
80+
return err
81+
}
82+
83+
logger := logrus.WithFields(logrus.Fields{})
84+
85+
logger.Info("pruning stranded bundles from the index")
86+
87+
indexPruner := indexer.NewIndexStrandedPruner(containertools.NewContainerTool(containerTool, containertools.PodmanTool), logger)
88+
89+
request := indexer.PruneStrandedFromIndexRequest{
90+
Generate: generate,
91+
FromIndex: fromIndex,
92+
BinarySourceImage: binaryImage,
93+
OutDockerfile: outDockerfile,
94+
Tag: tag,
95+
}
96+
97+
err = indexPruner.PruneStrandedFromIndex(request)
98+
if err != nil {
99+
return err
100+
}
101+
102+
return nil
103+
}

‎cmd/opm/registry/cmd.go

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ func NewOpmRegistryCmd() *cobra.Command {
2424
rootCmd.AddCommand(newRegistryAddCmd())
2525
rootCmd.AddCommand(newRegistryRmCmd())
2626
rootCmd.AddCommand(newRegistryPruneCmd())
27+
rootCmd.AddCommand(newRegistryPruneStrandedCmd())
2728

2829
return rootCmd
2930
}

‎cmd/opm/registry/prunestranded.go

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package registry
2+
3+
import (
4+
"github.com/operator-framework/operator-registry/pkg/lib/registry"
5+
6+
"github.com/sirupsen/logrus"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
func newRegistryPruneStrandedCmd() *cobra.Command {
11+
rootCmd := &cobra.Command{
12+
Use: "prune-stranded",
13+
Short: "prune an operator registry DB of stranded bundles",
14+
Long: `prune an operator registry DB of stranded bundles - bundles that are not associated with a particular package`,
15+
16+
PreRunE: func(cmd *cobra.Command, args []string) error {
17+
if debug, _ := cmd.Flags().GetBool("debug"); debug {
18+
logrus.SetLevel(logrus.DebugLevel)
19+
}
20+
return nil
21+
},
22+
23+
RunE: runRegistryPruneStrandedCmdFunc,
24+
}
25+
26+
rootCmd.Flags().Bool("debug", false, "enable debug logging")
27+
rootCmd.Flags().StringP("database", "d", "bundles.db", "relative path to database file")
28+
29+
return rootCmd
30+
}
31+
32+
func runRegistryPruneStrandedCmdFunc(cmd *cobra.Command, args []string) error {
33+
fromFilename, err := cmd.Flags().GetString("database")
34+
if err != nil {
35+
return err
36+
}
37+
38+
request := registry.PruneStrandedFromRegistryRequest{
39+
InputDatabase: fromFilename,
40+
}
41+
42+
logger := logrus.WithFields(logrus.Fields{})
43+
44+
logger.Info("pruning from the registry")
45+
46+
registryStrandedPruner := registry.NewRegistryStrandedPruner(logger)
47+
48+
err = registryStrandedPruner.PruneStrandedFromRegistry(request)
49+
if err != nil {
50+
return err
51+
}
52+
53+
return nil
54+
}

‎pkg/lib/indexer/indexer.go

+64-10
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,17 @@ const (
3535

3636
// ImageIndexer is a struct implementation of the Indexer interface
3737
type ImageIndexer struct {
38-
DockerfileGenerator containertools.DockerfileGenerator
39-
CommandRunner containertools.CommandRunner
40-
LabelReader containertools.LabelReader
41-
RegistryAdder registry.RegistryAdder
42-
RegistryDeleter registry.RegistryDeleter
43-
RegistryPruner registry.RegistryPruner
44-
RegistryDeprecator registry.RegistryDeprecator
45-
BuildTool containertools.ContainerTool
46-
PullTool containertools.ContainerTool
47-
Logger *logrus.Entry
38+
DockerfileGenerator containertools.DockerfileGenerator
39+
CommandRunner containertools.CommandRunner
40+
LabelReader containertools.LabelReader
41+
RegistryAdder registry.RegistryAdder
42+
RegistryDeleter registry.RegistryDeleter
43+
RegistryPruner registry.RegistryPruner
44+
RegistryStrandedPruner registry.RegistryStrandedPruner
45+
RegistryDeprecator registry.RegistryDeprecator
46+
BuildTool containertools.ContainerTool
47+
PullTool containertools.ContainerTool
48+
Logger *logrus.Entry
4849
}
4950

5051
// AddToIndexRequest defines the parameters to send to the AddToIndex API
@@ -168,6 +169,59 @@ func (i ImageIndexer) DeleteFromIndex(request DeleteFromIndexRequest) error {
168169
return nil
169170
}
170171

172+
// PruneStrandedFromIndexRequest defines the parameters to send to the PruneStrandedFromIndex API
173+
type PruneStrandedFromIndexRequest struct {
174+
Generate bool
175+
BinarySourceImage string
176+
FromIndex string
177+
OutDockerfile string
178+
Tag string
179+
}
180+
181+
// PruneStrandedFromIndex is an aggregate API used to generate a registry index image
182+
// that has removed stranded bundles from the index
183+
func (i ImageIndexer) PruneStrandedFromIndex(request PruneStrandedFromIndexRequest) error {
184+
buildDir, outDockerfile, cleanup, err := buildContext(request.Generate, request.OutDockerfile)
185+
defer cleanup()
186+
if err != nil {
187+
return err
188+
}
189+
190+
databasePath, err := i.extractDatabase(buildDir, request.FromIndex)
191+
if err != nil {
192+
return err
193+
}
194+
195+
// Run opm registry prune-stranded on the database
196+
pruneStrandedFromRegistryReq := registry.PruneStrandedFromRegistryRequest{
197+
InputDatabase: databasePath,
198+
}
199+
200+
// Delete the stranded bundles from the registry
201+
err = i.RegistryStrandedPruner.PruneStrandedFromRegistry(pruneStrandedFromRegistryReq)
202+
if err != nil {
203+
return err
204+
}
205+
206+
// generate the dockerfile
207+
dockerfile := i.DockerfileGenerator.GenerateIndexDockerfile(request.BinarySourceImage, databasePath)
208+
err = write(dockerfile, outDockerfile, i.Logger)
209+
if err != nil {
210+
return err
211+
}
212+
213+
if request.Generate {
214+
return nil
215+
}
216+
217+
// build the dockerfile
218+
err = build(outDockerfile, request.Tag, i.CommandRunner, i.Logger)
219+
if err != nil {
220+
return err
221+
}
222+
return nil
223+
}
224+
171225
// PruneFromIndexRequest defines the parameters to send to the PruneFromIndex API
172226
type PruneFromIndexRequest struct {
173227
Generate bool

‎pkg/lib/indexer/interfaces.go

+17
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,23 @@ func NewIndexExporter(containerTool containertools.ContainerTool, logger *logrus
6464
}
6565
}
6666

67+
// IndexStrandedPruner prunes operators out of an index
68+
type IndexStrandedPruner interface {
69+
PruneStrandedFromIndex(PruneStrandedFromIndexRequest) error
70+
}
71+
72+
func NewIndexStrandedPruner(containerTool containertools.ContainerTool, logger *logrus.Entry) IndexStrandedPruner {
73+
return ImageIndexer{
74+
DockerfileGenerator: containertools.NewDockerfileGenerator(logger),
75+
CommandRunner: containertools.NewCommandRunner(containerTool, logger),
76+
LabelReader: containertools.NewLabelReader(containerTool, logger),
77+
RegistryStrandedPruner: registry.NewRegistryStrandedPruner(logger),
78+
BuildTool: containerTool,
79+
PullTool: containerTool,
80+
Logger: logger,
81+
}
82+
}
83+
6784
// IndexPruner prunes operators out of an index
6885
type IndexPruner interface {
6986
PruneFromIndex(PruneFromIndexRequest) error

‎pkg/lib/registry/interfaces.go

+10
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ func NewRegistryDeleter(logger *logrus.Entry) RegistryDeleter {
2727
}
2828
}
2929

30+
type RegistryStrandedPruner interface {
31+
PruneStrandedFromRegistry(PruneStrandedFromRegistryRequest) error
32+
}
33+
34+
func NewRegistryStrandedPruner(logger *logrus.Entry) RegistryStrandedPruner {
35+
return RegistryUpdater{
36+
Logger: logger,
37+
}
38+
}
39+
3040
type RegistryPruner interface {
3141
PruneFromRegistry(PruneFromRegistryRequest) error
3242
}

‎pkg/lib/registry/registry.go

+34
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,40 @@ func (r RegistryUpdater) DeleteFromRegistry(request DeleteFromRegistryRequest) e
174174
}
175175
}
176176

177+
// remove any stranded bundles from the database
178+
// TODO: This is unnecessary if the db schema can prevent this orphaned data from existing
179+
remover := sqlite.NewSQLStrandedBundleRemover(dbLoader)
180+
if err := remover.Remove(); err != nil {
181+
return fmt.Errorf("error removing stranded packages from database: %s", err)
182+
}
183+
184+
return nil
185+
}
186+
187+
type PruneStrandedFromRegistryRequest struct {
188+
InputDatabase string
189+
}
190+
191+
func (r RegistryUpdater) PruneStrandedFromRegistry(request PruneStrandedFromRegistryRequest) error {
192+
db, err := sql.Open("sqlite3", request.InputDatabase)
193+
if err != nil {
194+
return err
195+
}
196+
defer db.Close()
197+
198+
dbLoader, err := sqlite.NewSQLLiteLoader(db)
199+
if err != nil {
200+
return err
201+
}
202+
if err := dbLoader.Migrate(context.TODO()); err != nil {
203+
return err
204+
}
205+
206+
remover := sqlite.NewSQLStrandedBundleRemover(dbLoader)
207+
if err := remover.Remove(); err != nil {
208+
return fmt.Errorf("error removing stranded packages from database: %s", err)
209+
}
210+
177211
return nil
178212
}
179213

‎pkg/registry/interface.go

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type Load interface {
1212
AddPackageChannels(manifest PackageManifest) error
1313
AddBundlePackageChannels(manifest PackageManifest, bundle *Bundle) error
1414
RemovePackage(packageName string) error
15+
RemoveStrandedBundles() ([]string, error)
1516
DeprecateBundle(path string) error
1617
ClearNonHeadBundles() error
1718
}

0 commit comments

Comments
 (0)
Please sign in to comment.