Skip to content

Commit

Permalink
Reduce verbosity of Pull command - simlple progress bar
Browse files Browse the repository at this point in the history
  • Loading branch information
tomekjarosik committed Aug 2, 2024
1 parent 4f54b60 commit 898c49b
Show file tree
Hide file tree
Showing 12 changed files with 95 additions and 19 deletions.
9 changes: 5 additions & 4 deletions cmd/geranos/cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import (

func NewListCommand() *cobra.Command {
var listCmd = &cobra.Command{
Use: "list",
Short: "List all OCI images in a specific repository.",
Long: `Lists all available OCI images in the specified container registry or repository, providing a quick overview of the stored images.`,
Args: cobra.ExactArgs(0),
Use: "list",
Short: "List all OCI images in a specific repository.",
Long: `Lists all available OCI images in the specified container registry or repository, providing a quick overview of the stored images.`,
Args: cobra.ExactArgs(0),
Aliases: []string{"ls"},
Run: func(cmd *cobra.Command, args []string) {
imagesDir := viper.GetString("images_directory")
if len(imagesDir) == 0 {
Expand Down
1 change: 1 addition & 0 deletions cmd/geranos/cmd/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func NewCmdPull() *cobra.Command {
opts := []transporter.Option{
transporter.WithImagesPath(imagesDir),
transporter.WithContext(cmd.Context()),
transporter.WithVerbose(viper.GetBool("verbose")),
}
return transporter.Pull(src, opts...)
},
Expand Down
15 changes: 7 additions & 8 deletions cmd/geranos/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import (
"fmt"
"github.com/google/go-containerregistry/cmd/crane/cmd"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os"
"os/signal"
"strings"
)

func InitializeCommands() *cobra.Command {
Expand All @@ -27,13 +27,12 @@ It relies on sparse files and Copy-on-Write filesystem features to optimize disk
},
}

// Customizing unknown command handling
rootCmd.SilenceErrors = false
rootCmd.SilenceUsage = false
rootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
fmt.Printf("Error: '%s' is not a known command\n\n", strings.Join(args, " "))
fmt.Println("Use 'geranos --help' for a list of available commands.")
})
// Define the --verbose global flag
var verbose bool
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "enable verbose output")

// Bind the verbose flag to Viper
viper.BindPFlag("verbose", rootCmd.PersistentFlags().Lookup("verbose"))

rootCmd.AddCommand(
NewCmdPull(),
Expand Down
8 changes: 8 additions & 0 deletions pkg/dirimage/dirimage.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,11 @@ type DirImage struct {
}

var _ v1.Image = (*DirImage)(nil)

func (d *DirImage) Length() int64 {
res := int64(0)
for _, d := range d.segmentDescriptors {
res += d.Length()
}
return res
}
7 changes: 7 additions & 0 deletions pkg/dirimage/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type options struct {
chunkSize int64
printf func(fmt string, argv ...any)
networkFailureRetryCount int
progress chan<- ProgressUpdate
}

type Option func(opts *options)
Expand Down Expand Up @@ -46,3 +47,9 @@ func WithLogFunction(log func(fmt string, args ...any)) Option {
o.printf = log
}
}

func WithProgressChannel(progress chan<- ProgressUpdate) Option {
return func(o *options) {
o.progress = progress
}
}
6 changes: 6 additions & 0 deletions pkg/dirimage/progress.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package dirimage

type ProgressUpdate struct {
BytesProcessed int64
BytesTotal int64
}
14 changes: 12 additions & 2 deletions pkg/dirimage/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ func truncateFiles(destinationDir string, segmentDescriptors []*filesegment.Desc
return nil
}

func sendProgressUpdate(progressChan chan<- ProgressUpdate, current, total int64) {
select {
case progressChan <- ProgressUpdate{
BytesProcessed: current,
BytesTotal: total,
}:
default:
}
}

func (di *DirImage) Write(ctx context.Context, destinationDir string, opt ...Option) error {
if di.Image == nil {
return errors.New("invalid image")
Expand All @@ -86,15 +96,15 @@ func (di *DirImage) Write(ctx context.Context, destinationDir string, opt ...Opt
Job Job
err error
}

bytesTotal := di.Length()
jobs := make(chan Job, opts.workersCount)
g, ctx := errgroup.WithContext(ctx)
layerOpts := []filesegment.LayerOpt{filesegment.WithLogFunction(opts.printf)}
for w := 0; w < opts.workersCount; w++ {
g.Go(func() error {
for job := range jobs {
atomic.AddInt64(&di.BytesReadCount, job.Descriptor.Length())

sendProgressUpdate(opts.progress, di.BytesReadCount, bytesTotal)
if filesegment.Matches(&job.Descriptor, destinationDir, layerOpts...) {
opts.printf("existing layer: %v\n", &job.Descriptor)
continue
Expand Down
3 changes: 3 additions & 0 deletions pkg/layout/layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ func (lm *Mapper) Write(ctx context.Context, img v1.Image, ref name.Reference) e
if err != nil {
return err
}
for _, layer := range manifest.Layers {
lm.stats.Add(&Statistics{SourceBytesCount: layer.Size})
}

bytesClonedCount, matchedSegmentsCount, err := lm.sketcher.Sketch(destinationDir, *manifest)
if err != nil {
Expand Down
4 changes: 0 additions & 4 deletions pkg/layout/progress.go

This file was deleted.

2 changes: 2 additions & 0 deletions pkg/layout/statistics.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package layout
import "sync/atomic"

type Statistics struct {
SourceBytesCount int64
BytesWrittenCount int64
BytesSkippedCount int64
BytesReadCount int64
Expand All @@ -18,6 +19,7 @@ func (s *Statistics) Add(other *Statistics) {
atomic.AddInt64(&s.BytesClonedCount, other.BytesClonedCount)
atomic.AddInt64(&s.CompressedBytesCount, other.CompressedBytesCount)
atomic.AddInt64(&s.MatchedSegmentsCount, other.MatchedSegmentsCount)
atomic.AddInt64(&s.SourceBytesCount, other.SourceBytesCount)
}

func (s *Statistics) Clear() {
Expand Down
8 changes: 8 additions & 0 deletions pkg/transporter/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type options struct {
remoteOptions []remote.Option
refValidation name.Option
workersCount int
verbose bool
ctx context.Context
}

Expand Down Expand Up @@ -57,6 +58,12 @@ func WithContext(ctx context.Context) Option {
}
}

func WithVerbose(verbose bool) Option {
return func(o *options) {
o.verbose = verbose
}
}

func makeOptions(opts ...Option) *options {
res := options{
imagesPath: mustExpandUser("~/.geranos/images"),
Expand All @@ -68,6 +75,7 @@ func makeOptions(opts ...Option) *options {
},
refValidation: name.StrictValidation,
workersCount: 8,
verbose: false,
ctx: context.Background(),
}
for _, o := range opts {
Expand Down
37 changes: 36 additions & 1 deletion pkg/transporter/pull.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
package transporter

import (
"fmt"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/tomekjarosik/geranos/pkg/dirimage"
"github.com/tomekjarosik/geranos/pkg/layout"
"log"
"strings"
)

func updateProgress(progress int64) {
buffer := new(strings.Builder)
fmt.Fprintf(buffer, "\rProgress: [%-100s] %d%%", strings.Repeat("=", int(progress)), progress)
fmt.Print(buffer.String())
}

func printProgress(progress <-chan dirimage.ProgressUpdate) {
last := int64(0)
for p := range progress {
current := 100 * p.BytesProcessed / p.BytesTotal
if current != last {
updateProgress(current)
}
last = current
}
fmt.Printf("\n")
}

func Pull(src string, opt ...Option) error {
opts := makeOptions(opt...)
ref, err := name.ParseReference(src, name.StrictValidation)
Expand All @@ -18,6 +40,19 @@ func Pull(src string, opt ...Option) error {
}
// Cache is not important if Sketch is working properly
//img = cache.Image(img, diskcache.NewFilesystemCache(opts.cachePath))
lm := layout.NewMapper(opts.imagesPath)
progress := make(chan dirimage.ProgressUpdate)
defer close(progress)

dirimageOptions := []dirimage.Option{
dirimage.WithProgressChannel(progress),
dirimage.WithLogFunction(func(fmt string, args ...any) {
}),
}
if opts.verbose {
dirimageOptions = append(dirimageOptions, dirimage.WithLogFunction(log.Printf))
}

lm := layout.NewMapper(opts.imagesPath, dirimageOptions...)
go printProgress(progress)
return lm.Write(opts.ctx, img, ref)
}

0 comments on commit 898c49b

Please sign in to comment.