-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved logging for store copy / Updated store info to handle multi-arch images #146
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,9 @@ import ( | |
"context" | ||
"encoding/json" | ||
"fmt" | ||
"strings" | ||
"text/tabwriter" | ||
"github.com/olekukonko/tablewriter" | ||
"os" | ||
"sort" | ||
|
||
ocispec "github.com/opencontainers/image-spec/specs-go/v1" | ||
"github.com/spf13/cobra" | ||
|
@@ -44,49 +45,109 @@ func InfoCmd(ctx context.Context, o *InfoOpts, s *store.Layout) error { | |
} | ||
defer rc.Close() | ||
|
||
var m ocispec.Manifest | ||
if err := json.NewDecoder(rc).Decode(&m); err != nil { | ||
return err | ||
} | ||
i := newItem(s, desc, m) | ||
var emptyItem item | ||
if i != emptyItem { | ||
items = append(items, i) | ||
// handle multi-arch images | ||
if desc.MediaType == consts.OCIImageIndexSchema || desc.MediaType == consts.DockerManifestListSchema2 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I imagine that these media type comparisons/checks will be with us for some years. I wonder if longer term than this particular change-set if it makes sense to unify them conceptually? i.e. for this one, possibly something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that idea. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea isn't for saving characters/lines of code so much as conceptual concision. |
||
var idx ocispec.Index | ||
if err := json.NewDecoder(rc).Decode(&idx); err != nil { | ||
return err | ||
} | ||
|
||
for _, internalDesc := range idx.Manifests { | ||
rc, err := s.Fetch(ctx, internalDesc) | ||
if err != nil { | ||
return err | ||
} | ||
defer rc.Close() | ||
|
||
var internalManifest ocispec.Manifest | ||
if err := json.NewDecoder(rc).Decode(&internalManifest); err != nil { | ||
return err | ||
} | ||
|
||
i := newItem(s, desc, internalManifest, internalDesc.Platform.Architecture) | ||
var emptyItem item | ||
if i != emptyItem { | ||
items = append(items, i) | ||
} | ||
} | ||
// handle single arch docker images | ||
} else if desc.MediaType == consts.DockerManifestSchema2 { | ||
var m ocispec.Manifest | ||
if err := json.NewDecoder(rc).Decode(&m); err != nil { | ||
return err | ||
} | ||
|
||
rc, err := s.FetchManifest(ctx, m) | ||
if err != nil { | ||
return err | ||
} | ||
defer rc.Close() | ||
|
||
// Unmarshal the OCI image content | ||
var internalManifest ocispec.Image | ||
if err := json.NewDecoder(rc).Decode(&internalManifest); err != nil { | ||
return err | ||
} | ||
|
||
i := newItem(s, desc, m, internalManifest.Architecture) | ||
var emptyItem item | ||
if i != emptyItem { | ||
items = append(items, i) | ||
} | ||
// handle the rest | ||
} else { | ||
var m ocispec.Manifest | ||
if err := json.NewDecoder(rc).Decode(&m); err != nil { | ||
return err | ||
} | ||
|
||
i := newItem(s, desc, m, "-") | ||
var emptyItem item | ||
if i != emptyItem { | ||
items = append(items, i) | ||
} | ||
} | ||
|
||
return nil | ||
}); err != nil { | ||
return err | ||
} | ||
|
||
// sort items by ref and arch | ||
sort.Sort(byReferenceAndArch(items)) | ||
|
||
var msg string | ||
switch o.OutputFormat { | ||
case "json": | ||
msg = buildJson(items...) | ||
|
||
fmt.Println(msg) | ||
default: | ||
msg = buildTable(items...) | ||
buildTable(items...) | ||
} | ||
fmt.Println(msg) | ||
return nil | ||
} | ||
|
||
func buildTable(items ...item) string { | ||
b := strings.Builder{} | ||
tw := tabwriter.NewWriter(&b, 1, 1, 3, ' ', 0) | ||
|
||
fmt.Fprintf(tw, "Reference\tType\t# Layers\tSize\n") | ||
fmt.Fprintf(tw, "---------\t----\t--------\t----\n") | ||
func buildTable(items ...item) { | ||
// Create a table for the results | ||
table := tablewriter.NewWriter(os.Stdout) | ||
table.SetHeader([]string{"Reference", "Type", "Arch", "# Layers", "Size"}) | ||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) | ||
table.SetRowLine(false) | ||
table.SetAutoMergeCellsByColumnIndex([]int{0}) | ||
|
||
for _, i := range items { | ||
if i.Type != "" { | ||
fmt.Fprintf(tw, "%s\t%s\t%d\t%s\n", | ||
i.Reference, i.Type, i.Layers, i.Size, | ||
) | ||
row := []string{ | ||
i.Reference, | ||
i.Type, | ||
i.Architecture, | ||
fmt.Sprintf("%d", i.Layers), | ||
i.Size, | ||
} | ||
table.Append(row) | ||
} | ||
} | ||
tw.Flush() | ||
return b.String() | ||
table.Render() | ||
} | ||
|
||
func buildJson(item ...item) string { | ||
|
@@ -98,16 +159,29 @@ func buildJson(item ...item) string { | |
} | ||
|
||
type item struct { | ||
Reference string | ||
Type string | ||
Layers int | ||
Size string | ||
Reference string | ||
Type string | ||
Architecture string | ||
Layers int | ||
Size string | ||
} | ||
|
||
type byReferenceAndArch []item | ||
|
||
func (a byReferenceAndArch) Len() int { return len(a) } | ||
func (a byReferenceAndArch) Swap(i, j int) { a[i], a[j] = a[j], a[i] } | ||
func (a byReferenceAndArch) Less(i, j int) bool { | ||
if a[i].Reference == a[j].Reference { | ||
return a[i].Architecture < a[j].Architecture | ||
} | ||
return a[i].Reference < a[j].Reference | ||
} | ||
|
||
func newItem(s *store.Layout, desc ocispec.Descriptor, m ocispec.Manifest) item { | ||
func newItem(s *store.Layout, desc ocispec.Descriptor, m ocispec.Manifest, arch string) item { | ||
// skip listing cosign items | ||
if desc.Annotations["kind"] == "dev.cosignproject.cosign/atts" || | ||
desc.Annotations["kind"] == "dev.cosignproject.cosign/sigs" || | ||
desc.Annotations["kind"] == "dev.cosignproject.cosign/sboms" { | ||
desc.Annotations["kind"] == "dev.cosignproject.cosign/sigs" || | ||
desc.Annotations["kind"] == "dev.cosignproject.cosign/sboms" { | ||
return item{} | ||
} | ||
|
||
|
@@ -135,10 +209,11 @@ func newItem(s *store.Layout, desc ocispec.Descriptor, m ocispec.Manifest) item | |
} | ||
|
||
return item{ | ||
Reference: ref.Name(), | ||
Type: ctype, | ||
Layers: len(m.Layers), | ||
Size: byteCountSI(size), | ||
Reference: ref.Name(), | ||
Type: ctype, | ||
Architecture: arch, | ||
Layers: len(m.Layers), | ||
Size: byteCountSI(size), | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MIT license, okay good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a fan of his tablewriter. I've used it in a few projects now.