mirror of
https://github.com/wagoodman/dive
synced 2026-03-14 14:25:50 +01:00
remove CNB specific logic for now
Signed-off-by: dwillist <dthornton@vmware.com>
This commit is contained in:
parent
9f745c7c8b
commit
adbd9d793f
15 changed files with 16 additions and 241 deletions
11
=
11
=
|
|
@ -1,11 +0,0 @@
|
|||
package componenets
|
||||
|
||||
import "github.com/rivo/tview"
|
||||
|
||||
type Keybinded interface {
|
||||
GetKeyBindings() []KeyBinding
|
||||
}
|
||||
|
||||
type KeyMenuPrimitive struct {
|
||||
*tview.TextView
|
||||
}
|
||||
1
Makefile
1
Makefile
|
|
@ -13,7 +13,6 @@ ci-unit-test:
|
|||
go test -cover -v -race ./...
|
||||
|
||||
ci-static-analysis:
|
||||
grep -R 'const allowTestDataCapture = false' runtime/ui/viewmodel
|
||||
go vet ./...
|
||||
@! gofmt -s -l . 2>&1 | grep -vE '^\.git/' | grep -vE '^\.cache/'
|
||||
golangci-lint run
|
||||
|
|
|
|||
|
|
@ -62,13 +62,7 @@ func doAnalyzeCmd(cmd *cobra.Command, args []string) {
|
|||
logrus.Error("unable to get 'ignore-errors' option:", err)
|
||||
}
|
||||
|
||||
cnb, err := cmd.PersistentFlags().GetBool("cnb")
|
||||
if err != nil {
|
||||
logrus.Error("unable to get 'cnb' option:", err)
|
||||
}
|
||||
|
||||
runtime.Run(runtime.Options{
|
||||
CNB: cnb,
|
||||
Ci: isCi,
|
||||
Source: sourceType,
|
||||
Image: imageStr,
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ func initCli() {
|
|||
rootCmd.Flags().StringVarP(&exportFile, "json", "j", "", "Skip the interactive TUI and write the layer analysis statistics to a given file.")
|
||||
rootCmd.Flags().StringVar(&ciConfigFile, "ci-config", ".dive-ci", "If CI=true in the environment, use the given yaml to drive validation rules.")
|
||||
|
||||
rootCmd.PersistentFlags().BoolP("cnb", "c", false, "show cloud native buildpack app metadata")
|
||||
rootCmd.Flags().String("lowestEfficiency", "0.9", "(only valid with --ci given) lowest allowable image efficiency (as a ratio between 0-1), otherwise CI validation will fail.")
|
||||
rootCmd.Flags().String("highestWastedBytes", "disabled", "(only valid with --ci given) highest allowable bytes wasted, otherwise CI validation will fail.")
|
||||
rootCmd.Flags().String("highestUserWastedPercent", "0.1", "(only valid with --ci given) highest allowable percentage of bytes wasted (as a ratio between 0-1), otherwise CI validation will fail.")
|
||||
|
|
@ -122,13 +121,11 @@ func initConfig() {
|
|||
tcell.NewEventKey(tcell.KeyCtrlB, rune(0), tcell.ModCtrl),
|
||||
))
|
||||
|
||||
|
||||
viper.SetDefault("keybinding.toggle-added-files", components.NewKeyBinding(
|
||||
"Added",
|
||||
tcell.NewEventKey(tcell.KeyCtrlA, rune(0), tcell.ModCtrl),
|
||||
))
|
||||
|
||||
|
||||
viper.SetDefault("keybinding.toggle-removed-files", components.NewKeyBinding(
|
||||
"Removed",
|
||||
tcell.NewEventKey(tcell.KeyCtrlR, rune(0), tcell.ModCtrl),
|
||||
|
|
|
|||
|
|
@ -1,85 +0,0 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/buildpacks/lifecycle"
|
||||
"github.com/buildpacks/pack"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wagoodman/dive/dive/filetree"
|
||||
)
|
||||
|
||||
var ErrMissingCNBMetadata = fmt.Errorf("missing CNB metadata")
|
||||
|
||||
func (img *Image) CNBAnalyze(layerAnalysis *AnalysisResult) (*AnalysisResult, error) {
|
||||
client, err := pack.NewClient()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create pack client: %s", err)
|
||||
}
|
||||
|
||||
logrus.Debugf("Inspecting image: %s", img.Name)
|
||||
imageInfo, err := client.InspectImage(img.Name, true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to retrieve %s image info: %s", img.Name, err)
|
||||
}
|
||||
empty := lifecycle.RunImageMetadata{}
|
||||
if imageInfo.Base == empty {
|
||||
return nil, ErrMissingCNBMetadata
|
||||
}
|
||||
logrus.Debugf("image info %#v", imageInfo)
|
||||
|
||||
newLayers := []*Layer{}
|
||||
newRefTree := []*filetree.FileTree{}
|
||||
|
||||
if len(layerAnalysis.Layers) != len(layerAnalysis.RefTrees) {
|
||||
return nil, fmt.Errorf("mismatched lengths %s vs %s", len(layerAnalysis.Layers), len(layerAnalysis.RefTrees))
|
||||
}
|
||||
|
||||
var curLayer *Layer = nil
|
||||
var curRefTree *filetree.FileTree = nil
|
||||
var isStack = true
|
||||
for layerIdx, layer := range layerAnalysis.Layers {
|
||||
rTree := layerAnalysis.RefTrees[layerIdx]
|
||||
if curLayer == nil {
|
||||
curLayer = layer
|
||||
curRefTree = rTree
|
||||
continue
|
||||
}
|
||||
if isStack { // in stack still
|
||||
curLayer.Size += layer.Size
|
||||
_, err = curRefTree.Stack(rTree)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error to stacking trees")
|
||||
}
|
||||
}
|
||||
if layer.Digest == imageInfo.Base.TopLayer { // end of stack
|
||||
newLayers = append(newLayers, curLayer)
|
||||
newRefTree = append(newRefTree, curRefTree)
|
||||
isStack = false
|
||||
}
|
||||
if !isStack {
|
||||
newLayers = append(newLayers, layer)
|
||||
newRefTree = append(newRefTree, rTree)
|
||||
}
|
||||
}
|
||||
|
||||
layerAnalysis.RefTrees = newRefTree
|
||||
layerAnalysis.Layers = newLayers
|
||||
layerAnalysis.BOMMapping = buildBOMMapping(layerAnalysis.Layers, imageInfo)
|
||||
|
||||
return layerAnalysis, nil
|
||||
}
|
||||
|
||||
func buildBOMMapping(layers []*Layer, labelMetadata *pack.ImageInfo) map[string]lifecycle.BOMEntry {
|
||||
result := make(map[string]lifecycle.BOMEntry, 0)
|
||||
for layerIndex, layer := range layers {
|
||||
result[layer.Digest] = lifecycle.BOMEntry{
|
||||
Require: lifecycle.Require{},
|
||||
Buildpack: lifecycle.Buildpack{
|
||||
ID: fmt.Sprintf("buildpack metadata for layer: %d", layerIndex),
|
||||
Version: "1.2.3",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import (
|
|||
)
|
||||
|
||||
type Image struct {
|
||||
Name string
|
||||
Name string
|
||||
Trees []*filetree.FileTree
|
||||
Layers []*Layer
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ func (img *Image) Analyze() (*AnalysisResult, error) {
|
|||
}
|
||||
|
||||
return &AnalysisResult{
|
||||
ImageName: img.Name,
|
||||
ImageName: img.Name,
|
||||
Layers: img.Layers,
|
||||
RefTrees: img.Trees,
|
||||
Efficiency: efficiency,
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -78,4 +78,4 @@ replace github.com/golangci/lint-1 => github.com/golangci/lint-1 v0.0.0-20190420
|
|||
|
||||
replace mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34
|
||||
|
||||
replace github.com/rivo/tview => /home/deek/workspace/VMware/dwillist/tview
|
||||
replace github.com/rivo/tview => github.com/dwillist/tview v0.0.0-20210114215028-5f3be7b5a4ec
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -147,6 +147,8 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ
|
|||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dwillist/tview v0.0.0-20210114215028-5f3be7b5a4ec h1:1BBnesecQR96WbK9DYDtGeFjXZw/97jl7JFNn5Xih+c=
|
||||
github.com/dwillist/tview v0.0.0-20210114215028-5f3be7b5a4ec/go.mod h1:0ha5CGekam8ZV1kxkBxSlh7gfQ7YolUj2P/VruwH0QY=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
type Options struct {
|
||||
Ci bool
|
||||
CNB bool
|
||||
Image string
|
||||
Source dive.ImageSource
|
||||
IgnoreErrors bool
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package runtime
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
|
|
@ -52,17 +51,6 @@ func run(enableUi bool, options Options, imageResolver image.Resolver, events ev
|
|||
return
|
||||
}
|
||||
|
||||
if options.CNB {
|
||||
events.message(utils.TitleFormat("Analyzing Cloud Native Buildpacks image..."))
|
||||
analysis, err = img.CNBAnalyze(analysis)
|
||||
if errors.Is(err, image.ErrMissingCNBMetadata) {
|
||||
options.CNB = false
|
||||
} else if err != nil {
|
||||
events.exitWithErrorMessage("cannot analyze image", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if doExport {
|
||||
events.message(utils.TitleFormat(fmt.Sprintf("Exporting image to '%s'...", options.ExportFile)))
|
||||
bytes, err := export.NewExport(analysis).Marshal()
|
||||
|
|
@ -115,7 +103,7 @@ func run(enableUi bool, options Options, imageResolver image.Resolver, events ev
|
|||
}
|
||||
|
||||
if enableUi {
|
||||
err = ui.Run(analysis, treeStack, options.CNB)
|
||||
err = ui.Run(analysis, treeStack)
|
||||
if err != nil {
|
||||
zap.S().Error("run info exit: ", err.Error())
|
||||
events.exitWithError(err)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ type UI struct {
|
|||
filterView tview.Primitive
|
||||
}
|
||||
|
||||
func newApp(app *tview.Application, analysis *image.AnalysisResult, cache filetree.Comparer, isCNB bool) (*UI, error) {
|
||||
func newApp(app *tview.Application, analysis *image.AnalysisResult, cache filetree.Comparer) (*UI, error) {
|
||||
var err error
|
||||
once.Do(func() {
|
||||
config := components.NewKeyConfig()
|
||||
|
|
@ -41,21 +41,11 @@ func newApp(app *tview.Application, analysis *image.AnalysisResult, cache filetr
|
|||
|
||||
//initialize viewmodels
|
||||
filterViewModel := viewmodels.NewFilterViewModel(nil)
|
||||
var layerModel viewmodels.LayersModel
|
||||
var layerDetailsBox *components.Wrapper
|
||||
|
||||
// TODO extract the CNB specific logic here for now...
|
||||
if isCNB {
|
||||
cnbLayerViewModel := viewmodels.NewCNBLayersViewModel(analysis.Layers, analysis.BOMMapping)
|
||||
cnbLayerDetailsView := components.NewCNBLayerDetailsView(cnbLayerViewModel).Setup()
|
||||
layerModel = cnbLayerViewModel
|
||||
layerDetailsBox = components.NewWrapper("CNB Layer Details", "", cnbLayerDetailsView).Setup()
|
||||
} else {
|
||||
layerViewModel := viewmodels.NewLayersViewModel(analysis.Layers)
|
||||
regularLayerDetailsView := components.NewLayerDetailsView(layerViewModel).Setup()
|
||||
layerModel = layerViewModel
|
||||
layerDetailsBox = components.NewWrapper("Layer Details", "", regularLayerDetailsView).Setup()
|
||||
}
|
||||
layerModel := viewmodels.NewLayersViewModel(analysis.Layers)
|
||||
regularLayerDetailsView := components.NewLayerDetailsView(layerModel).Setup()
|
||||
layerDetailsBox := components.NewWrapper("Layer Details", "", regularLayerDetailsView).Setup()
|
||||
layerDetailsBox.SetVisibility(components.MinHeightVisibility(10))
|
||||
|
||||
//layerViewModel := viewmodels.NewLayersViewModel(analysis.Layers)
|
||||
|
|
@ -171,7 +161,7 @@ func newApp(app *tview.Application, analysis *image.AnalysisResult, cache filetr
|
|||
}
|
||||
|
||||
// Run is the UI entrypoint.
|
||||
func Run(analysis *image.AnalysisResult, treeStack filetree.Comparer, isCNB bool) error {
|
||||
func Run(analysis *image.AnalysisResult, treeStack filetree.Comparer) error {
|
||||
cfg := zap.NewDevelopmentConfig()
|
||||
os.MkdirAll("/tmp/dive", os.ModePerm)
|
||||
cfg.OutputPaths = []string{"/tmp/dive/debug.out"}
|
||||
|
|
@ -186,7 +176,7 @@ func Run(analysis *image.AnalysisResult, treeStack filetree.Comparer, isCNB bool
|
|||
zap.S().Info("Starting Hidden Flex Program")
|
||||
|
||||
app := tview.NewApplication()
|
||||
_, err = newApp(app, analysis, treeStack, isCNB)
|
||||
_, err = newApp(app, analysis, treeStack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/buildpacks/lifecycle"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
"github.com/wagoodman/dive/dive/image"
|
||||
)
|
||||
|
||||
type CNBLayerDetailModel interface {
|
||||
GetCurrentLayer() *image.Layer
|
||||
GetBOMFromDigest(layerSha string) lifecycle.BOMEntry
|
||||
}
|
||||
|
||||
// TODO make this scrollable
|
||||
type CNBLayerDetailsView struct {
|
||||
*tview.TextView
|
||||
layerIndex int
|
||||
CNBLayerDetailModel
|
||||
}
|
||||
|
||||
func NewCNBLayerDetailsView(model CNBLayerDetailModel) *CNBLayerDetailsView {
|
||||
return &CNBLayerDetailsView{
|
||||
TextView: tview.NewTextView(),
|
||||
CNBLayerDetailModel: model,
|
||||
}
|
||||
}
|
||||
|
||||
func (lv *CNBLayerDetailsView) Setup() *CNBLayerDetailsView {
|
||||
lv.SetTitle("Layer Details").SetTitleAlign(tview.AlignLeft)
|
||||
lv.SetDynamicColors(true).SetBorder(true)
|
||||
return lv
|
||||
}
|
||||
|
||||
func (lv *CNBLayerDetailsView) Draw(screen tcell.Screen) {
|
||||
curLayer := lv.CNBLayerDetailModel.GetCurrentLayer()
|
||||
curBOM := lv.GetBOMFromDigest(curLayer.Digest)
|
||||
displayText := layerCNBDetailsText(curLayer, curBOM)
|
||||
lv.SetText(displayText)
|
||||
lv.TextView.Draw(screen)
|
||||
}
|
||||
|
||||
|
||||
func (lv *CNBLayerDetailsView) GetKeyBindings() []KeyBindingDisplay {
|
||||
return []KeyBindingDisplay {}
|
||||
}
|
||||
|
||||
func layerCNBDetailsText(layer *image.Layer, bom lifecycle.BOMEntry) string {
|
||||
lines := []string{}
|
||||
if layer.Names != nil && len(layer.Names) > 0 {
|
||||
lines = append(lines, boldString("Tags: ")+strings.Join(layer.Names, ", "))
|
||||
} else {
|
||||
lines = append(lines, boldString("Tags: ")+"(none)")
|
||||
}
|
||||
lines = append(lines, boldString("Id: ")+layer.Id)
|
||||
lines = append(lines, layer.Command)
|
||||
lines = append(lines, boldString("BOM: ")+fmt.Sprintf("Entry for: %s", bom.Buildpack.String()))
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func (lv *CNBLayerDetailsView) getBox() *tview.Box {
|
||||
return lv.Box
|
||||
}
|
||||
|
||||
func (lv *CNBLayerDetailsView) getDraw() drawFn {
|
||||
return lv.Draw
|
||||
}
|
||||
|
||||
func (lv *CNBLayerDetailsView) getInputWrapper() inputFn {
|
||||
return lv.InputHandler
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/rivo/tview"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
|
@ -25,7 +27,7 @@ func (d *DiveApplication) GetKeyBindings() []KeyBindingDisplay {
|
|||
result := []KeyBindingDisplay{}
|
||||
for i := 0; i < len(d.bindings); i++ {
|
||||
binding := d.bindings[i]
|
||||
logrus.Debug("adding keybinding with name %s", binding.Display)
|
||||
logrus.Debug(fmt.Sprintf("adding keybinding with name %s", binding.Display))
|
||||
result = append(result, KeyBindingDisplay{KeyBinding: &binding, Selected: false})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
package viewmodels
|
||||
|
||||
import (
|
||||
"github.com/buildpacks/lifecycle"
|
||||
"github.com/wagoodman/dive/dive/image"
|
||||
)
|
||||
|
||||
type CNBLayersViewModel struct {
|
||||
*LayersViewModel
|
||||
bomMapping map[string]lifecycle.BOMEntry
|
||||
}
|
||||
|
||||
func NewCNBLayersViewModel(layers []*image.Layer, bomMapping map[string]lifecycle.BOMEntry) *CNBLayersViewModel {
|
||||
return &CNBLayersViewModel{
|
||||
LayersViewModel: NewLayersViewModel(layers),
|
||||
bomMapping: bomMapping,
|
||||
}
|
||||
}
|
||||
|
||||
func (cvm *CNBLayersViewModel) GetBOMFromDigest(layerSha string) lifecycle.BOMEntry {
|
||||
result, ok := cvm.bomMapping[layerSha]
|
||||
if !ok {
|
||||
return lifecycle.BOMEntry{}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ func (lm *LayersViewModel) GetCompareIndicies() filetree.TreeIndexKey {
|
|||
|
||||
func (lm *LayersViewModel) SetLayerIndex(index int) bool {
|
||||
if 0 <= index && index < len(lm.layers) {
|
||||
logrus.Debug("setting index, old: %d, new: %d", lm.index, index)
|
||||
logrus.Debug(fmt.Sprintf("setting index, old: %d, new: %d", lm.index, index))
|
||||
lm.index = index
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue