diff --git a/cmd/luna/app/app.go b/cmd/luna/app/app.go index 4f3fe48..60bc446 100644 --- a/cmd/luna/app/app.go +++ b/cmd/luna/app/app.go @@ -1,11 +1,7 @@ package app import ( - "context" - "errors" - "fmt" "os" - "time" "github.com/spf13/cobra" @@ -30,87 +26,10 @@ imports/exports of vulnerability store, management of scanning tasks, etc. }, } cmd.AddCommand(versionCmd) - var dsn string - var exportFrom string - var exportFileName string - exportCmd := &cobra.Command{ - Use: "export", - Long: "Export vulnerability update package, so other luna can import this package to skip manual updates", - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - var from time.Time - if exportFrom != "" { - var err error - const layout = "2006-01-02" - from, err = time.Parse(layout, exportFrom) - if err != nil { - return fmt.Errorf("invalid --from flag, valid values like %s", layout) - } - } - if exportFileName == "" { - return errors.New("-o is required") - } - if err := Export(ctx, dsn, from, exportFileName); err != nil { - return err - } - return nil - }, - } - - exportCmd.PersistentFlags().StringVarP(&dsn, "dsn", "", "", "DSN to connect to postgres") - exportCmd.PersistentFlags().StringVarP(&exportFrom, "from", "", "", "Export since time, leaving empty will export everything. Example: 2021-01-12") - exportCmd.PersistentFlags().StringVarP(&exportFileName, "out", "o", "", "Output filename for the export, better end with .gz") - cmd.AddCommand(exportCmd) - - var importFileName string - importCmd := &cobra.Command{ - Use: "import", - Long: "Import offline update package from a file, and load it into new database. Migrations will be done automatically", - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - if importFileName == "" { - return errors.New("-i is required") - } - if dsn == "" { - return errors.New("--dsn is required") - } - return Import(ctx, importFileName, dsn) - }, - } - importCmd.PersistentFlags().StringVarP(&dsn, "dsn", "", "", "DSN for the database to be migrated") - importCmd.PersistentFlags().StringVarP(&importFileName, "in", "i", "", "Input update package") - cmd.AddCommand(importCmd) - - inspectCmd := &cobra.Command{ - Use: "inspect", - Long: "Inspect image", - RunE: func(cmd *cobra.Command, args []string) error { - fmt.Println("Args:", args) - m, err := Inspect(context.Background(), args[0]) - if err != nil { - return err - } - raw, _ := json.MarshalIndent(m, "", " ") - fmt.Println(string(raw)) - return nil - }, - } - cmd.AddCommand(inspectCmd) - - scanCmd := &cobra.Command{ - Use: "scan", - Long: "Scan image", - RunE: func(cmd *cobra.Command, args []string) error { - if dsn == "" { - return errors.New("missing dsn argument") - } - err := ScanLocal(context.Background(), args[0], dsn) - return err - }, - } - scanCmd.PersistentFlags().StringVarP(&dsn, "dsn", "", "", "DSN for the database to be migrated") - cmd.AddCommand(scanCmd) - + cmd.AddCommand(NewExportCommand()) + cmd.AddCommand(NewImportCommand()) + cmd.AddCommand(NewInspectCommand()) + cmd.AddCommand(NewScanCommand()) cmd.AddCommand(NewUpdateCommand()) cmd.AddCommand(NewInfraScanCommand()) diff --git a/cmd/luna/app/export.go b/cmd/luna/app/export.go index 312b462..1b6e2d8 100644 --- a/cmd/luna/app/export.go +++ b/cmd/luna/app/export.go @@ -12,6 +12,7 @@ import ( "github.com/pkg/errors" "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" + "github.com/spf13/cobra" "github.com/beacon/luna/pkg/dao" ) @@ -40,6 +41,41 @@ type DiskEntry struct { Vuln *claircore.Vulnerability } +// NewExportCommand export command +func NewExportCommand() *cobra.Command { + var dsn string + var exportFrom string + var exportFileName string + exportCmd := &cobra.Command{ + Use: "export", + Long: "Export vulnerability update package, so other luna can import this package to skip manual updates", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + var from time.Time + if exportFrom != "" { + var err error + const layout = "2006-01-02" + from, err = time.Parse(layout, exportFrom) + if err != nil { + return fmt.Errorf("invalid --from flag, valid values like %s", layout) + } + } + if exportFileName == "" { + return errors.New("-o is required") + } + if err := Export(ctx, dsn, from, exportFileName); err != nil { + return err + } + return nil + }, + } + + exportCmd.PersistentFlags().StringVarP(&dsn, "dsn", "", "", "DSN to connect to postgres") + exportCmd.PersistentFlags().StringVarP(&exportFrom, "from", "", "", "Export since time, leaving empty will export everything. Example: 2021-01-12") + exportCmd.PersistentFlags().StringVarP(&exportFileName, "out", "o", "", "Output filename for the export, better end with .gz") + return exportCmd +} + // Export vulnerability updates func Export(ctx context.Context, dsn string, from time.Time, fileName string) error { start := time.Now() diff --git a/cmd/luna/app/import.go b/cmd/luna/app/import.go index f21fc68..51cb502 100644 --- a/cmd/luna/app/import.go +++ b/cmd/luna/app/import.go @@ -9,8 +9,32 @@ import ( "github.com/quay/claircore/libvuln" "github.com/quay/zlog" "github.com/rs/zerolog" + "github.com/spf13/cobra" ) +func NewImportCommand() *cobra.Command { + // TODO: move it into a single package + var importFileName string + var dsn string + importCmd := &cobra.Command{ + Use: "import", + Long: "Import offline update package from a file, and load it into new database. Migrations will be done automatically", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + if importFileName == "" { + return errors.New("-i is required") + } + if dsn == "" { + return errors.New("--dsn is required") + } + return Import(ctx, importFileName, dsn) + }, + } + importCmd.PersistentFlags().StringVarP(&dsn, "dsn", "", "", "DSN for the database to be migrated") + importCmd.PersistentFlags().StringVarP(&importFileName, "in", "i", "", "Input update package") + return importCmd +} + // Import import an update package and load it into specified database func Import(ctx context.Context, srcFile string, dsn string) error { opts := &libvuln.Opts{ diff --git a/cmd/luna/app/scan.go b/cmd/luna/app/scan.go index eece1a2..9293000 100644 --- a/cmd/luna/app/scan.go +++ b/cmd/luna/app/scan.go @@ -23,12 +23,50 @@ import ( "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/google/uuid" "github.com/pkg/errors" + "github.com/spf13/cobra" "github.com/quay/claircore" "github.com/quay/claircore/libindex" "github.com/quay/claircore/libvuln" ) +// NewInspectCommand inspect +func NewInspectCommand() *cobra.Command { + inspectCmd := &cobra.Command{ + Use: "inspect", + Long: "Inspect image", + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("Args:", args) + m, err := Inspect(context.Background(), args[0]) + if err != nil { + return err + } + raw, _ := json.MarshalIndent(m, "", " ") + fmt.Println(string(raw)) + return nil + }, + } + return inspectCmd +} + +// NewScanCommand scan +func NewScanCommand() *cobra.Command { + var dsn string + scanCmd := &cobra.Command{ + Use: "scan", + Long: "Scan image", + RunE: func(cmd *cobra.Command, args []string) error { + if dsn == "" { + return errors.New("missing dsn argument") + } + err := ScanLocal(context.Background(), args[0], dsn) + return err + }, + } + scanCmd.PersistentFlags().StringVarP(&dsn, "dsn", "", "", "DSN for the database to be migrated") + return scanCmd +} + // Scan an image func Scan(ctx context.Context, imageRef string, remote bool) error { return nil diff --git a/pkg/infrascanner/dpkg/dpkg.go b/pkg/infrascanner/dpkg/dpkg.go index 9a6297e..3628744 100644 --- a/pkg/infrascanner/dpkg/dpkg.go +++ b/pkg/infrascanner/dpkg/dpkg.go @@ -102,43 +102,8 @@ func (ps *Scanner) Scan(ctx context.Context) ([]*claircore.Package, error) { pkgs = append(pkgs, p) } - infoDir := filepath.Join(p, "info") - const suffix = ".md5sums" - infos, err := ioutil.ReadDir(infoDir) - if err != nil { - return nil, errors.Wrap(err, "failed to read dir:"+infoDir) - } - for _, info := range infos { - if !strings.HasSuffix(info.Name(), suffix) { - continue - } - n := filepath.Base(info.Name()) - n = strings.TrimSuffix(n, suffix) - if i := strings.IndexRune(n, ':'); i != -1 { - n = n[:i] - } - p, ok := found[n] - if !ok { - zlog.Debug(ctx). - Str("package", n). - Msg("extra metadata found, ignoring") - continue - } - sumFileName := path.Join(infoDir, info.Name()) - sumFile, err := os.Open(sumFileName) - if err != nil { - return nil, errors.Wrap(err, "failed to open sum file:"+sumFileName) - } - hash := md5.New() - if _, err := io.Copy(hash, sumFile); err != nil { - zlog.Warn(ctx). - Err(err). - Str("package", n). - Msg("unable to read package metadata") - continue - } - sumFile.Close() - p.RepositoryHint = hex.EncodeToString(hash.Sum(nil)) + if err := addFileHash(ctx, found, filepath.Join(p, "info")); err != nil { + return nil, err } zlog.Debug(ctx). Int("count", len(found)). @@ -147,3 +112,44 @@ func (ps *Scanner) Scan(ctx context.Context) ([]*claircore.Package, error) { return pkgs, nil } + +func addFileHash(ctx context.Context, metamap map[string]*claircore.Package, infoDir string) error { + infos, err := ioutil.ReadDir(infoDir) + if err != nil { + return errors.Wrap(err, "failed to read dir:"+infoDir) + } + const suffix = ".md5sums" + for _, info := range infos { + if !strings.HasSuffix(info.Name(), suffix) { + continue + } + n := filepath.Base(info.Name()) + n = strings.TrimSuffix(n, suffix) + if i := strings.IndexRune(n, ':'); i != -1 { + n = n[:i] + } + p, ok := metamap[n] + if !ok { + zlog.Debug(ctx). + Str("package", n). + Msg("extra metadata found, ignoring") + continue + } + sumFileName := path.Join(infoDir, info.Name()) + sumFile, err := os.Open(sumFileName) + if err != nil { + return errors.Wrap(err, "failed to open sum file:"+sumFileName) + } + hash := md5.New() + if _, err := io.Copy(hash, sumFile); err != nil { + zlog.Warn(ctx). + Err(err). + Str("package", n). + Msg("unable to read package metadata") + continue + } + sumFile.Close() + p.RepositoryHint = hex.EncodeToString(hash.Sum(nil)) + } + return nil +}