Skip to content

Commit 48d711e

Browse files
authored
deploy: add multi-writer for progress (#318)
There are still some minor visual glitches. See the examples for reference. ## Image load (containerd): [![asciicast](https://asciinema.org/a/W3br2Ttq2JrF5yQbCyUQJbNBe.svg)](https://asciinema.org/a/W3br2Ttq2JrF5yQbCyUQJbNBe) ## Image load (docker): [![asciicast](https://asciinema.org/a/FpOJAaQzqa1oA43a67rDPDXpu.svg)](https://asciinema.org/a/FpOJAaQzqa1oA43a67rDPDXpu) ## Image push: [![asciicast](https://asciinema.org/a/NjVjD8rI1QLaXC0Szx5w2Jo5d.svg)](https://asciinema.org/a/NjVjD8rI1QLaXC0Szx5w2Jo5d) ## Broken image push (minor visual glitch): [![asciicast](https://asciinema.org/a/J0rA0oCswztkZsbSbDIFui3aw.svg)](https://asciinema.org/a/J0rA0oCswztkZsbSbDIFui3aw)
1 parent 5232234 commit 48d711e

File tree

9 files changed

+329
-139
lines changed

9 files changed

+329
-139
lines changed

img_tool/MODULE.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ go_sdk.from_file(
2727

2828
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
2929
go_deps.from_file(go_mod = "//:go.mod")
30-
use_repo(go_deps, "com_github_aws_aws_sdk_go_v2", "com_github_aws_aws_sdk_go_v2_config", "com_github_aws_aws_sdk_go_v2_service_s3", "com_github_containerd_containerd_api", "com_github_containerd_platforms", "com_github_containerd_stargz_snapshotter_estargz", "com_github_google_uuid", "com_github_klauspost_compress", "com_github_klauspost_pgzip", "com_github_malt3_go_containerregistry", "com_github_opencontainers_go_digest", "com_github_opencontainers_image_spec", "com_github_schollz_progressbar_v3", "com_google_cloud_go_longrunning", "org_golang_google_genproto_googleapis_api", "org_golang_google_genproto_googleapis_bytestream", "org_golang_google_genproto_googleapis_rpc", "org_golang_google_grpc", "org_golang_google_protobuf", "org_golang_x_sync")
30+
use_repo(go_deps, "com_github_aws_aws_sdk_go_v2", "com_github_aws_aws_sdk_go_v2_config", "com_github_aws_aws_sdk_go_v2_service_s3", "com_github_containerd_containerd_api", "com_github_containerd_platforms", "com_github_containerd_stargz_snapshotter_estargz", "com_github_google_uuid", "com_github_jedib0t_go_pretty_v6", "com_github_klauspost_compress", "com_github_klauspost_pgzip", "com_github_malt3_go_containerregistry", "com_github_opencontainers_go_digest", "com_github_opencontainers_image_spec", "com_google_cloud_go_longrunning", "org_golang_google_genproto_googleapis_api", "org_golang_google_genproto_googleapis_bytestream", "org_golang_google_genproto_googleapis_rpc", "org_golang_google_grpc", "org_golang_google_protobuf", "org_golang_x_sync", "org_golang_x_term")

img_tool/go.mod

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ require (
1212
github.com/containerd/platforms v1.0.0-rc.2
1313
github.com/containerd/stargz-snapshotter/estargz v0.18.1
1414
github.com/google/uuid v1.6.0
15+
github.com/jedib0t/go-pretty/v6 v6.7.5
1516
github.com/klauspost/compress v1.18.2
1617
github.com/klauspost/pgzip v1.2.6
1718
github.com/malt3/go-containerregistry v0.0.0-20250724131542-7e98b20e9b45
@@ -48,17 +49,15 @@ require (
4849
github.com/docker/cli v28.2.2+incompatible // indirect
4950
github.com/docker/distribution v2.8.3+incompatible // indirect
5051
github.com/docker/docker-credential-helpers v0.9.3 // indirect
51-
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
52+
github.com/mattn/go-runewidth v0.0.16 // indirect
5253
github.com/mitchellh/go-homedir v1.1.0 // indirect
5354
github.com/pkg/errors v0.9.1 // indirect
5455
github.com/rivo/uniseg v0.4.7 // indirect
55-
github.com/schollz/progressbar/v3 v3.18.0
5656
github.com/sirupsen/logrus v1.9.3 // indirect
57-
github.com/stretchr/testify v1.10.0 // indirect
5857
github.com/vbatts/tar-split v0.12.2 // indirect
5958
golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
6059
golang.org/x/oauth2 v0.32.0 // indirect
6160
golang.org/x/sys v0.37.0 // indirect
62-
golang.org/x/term v0.36.0 // indirect
61+
golang.org/x/term v0.36.0
6362
golang.org/x/text v0.30.0 // indirect
6463
)

img_tool/go.sum

Lines changed: 3 additions & 82 deletions
Large diffs are not rendered by default.

img_tool/pkg/docker/stream.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package docker
22

33
import (
44
"archive/tar"
5+
"context"
56
"encoding/json"
67
"fmt"
78
"io"
@@ -58,7 +59,7 @@ func (t *TarWriter) WriteConfig(configData []byte) error {
5859
}
5960

6061
// WriteLayer streams a layer to the tar
61-
func (t *TarWriter) WriteLayer(layerDigest registryv1.Hash, size int64, reader io.Reader) error {
62+
func (t *TarWriter) WriteLayer(ctx context.Context, layerDigest registryv1.Hash, size int64, reader io.Reader) error {
6263
layerDir := layerDigest.Hex
6364
layerPath := path.Join(layerDir, "layer.tar")
6465

@@ -73,7 +74,7 @@ func (t *TarWriter) WriteLayer(layerDigest registryv1.Hash, size int64, reader i
7374
// Create directory entry
7475
if err := t.tw.WriteHeader(&tar.Header{
7576
Name: layerDir + "/",
76-
Mode: 0755,
77+
Mode: 0o755,
7778
Typeflag: tar.TypeDir,
7879
}); err != nil {
7980
return fmt.Errorf("writing layer directory: %w", err)
@@ -87,15 +88,18 @@ func (t *TarWriter) WriteLayer(layerDigest registryv1.Hash, size int64, reader i
8788
// Write layer content
8889
hdr := &tar.Header{
8990
Name: layerPath,
90-
Mode: 0644,
91+
Mode: 0o644,
9192
Size: size,
9293
}
9394
if err := t.tw.WriteHeader(hdr); err != nil {
9495
return fmt.Errorf("writing layer header: %w", err)
9596
}
9697

97-
// Stream the layer content
98-
pw := progress.Writer(size, layerDigest.Hex[:12])
98+
// Stream the layer content with progress tracking
99+
pw, err := progress.Writer(ctx, size, layerDigest.Hex[:12])
100+
if err != nil {
101+
return fmt.Errorf("creating progress writer: %w", err)
102+
}
99103
n, err := io.Copy(io.MultiWriter(t.tw, pw), reader)
100104
if err != nil {
101105
return fmt.Errorf("streaming layer content: %w", err)
@@ -134,7 +138,7 @@ func (t *TarWriter) Finalize() error {
134138
func (t *TarWriter) writeFile(name string, data []byte) error {
135139
hdr := &tar.Header{
136140
Name: name,
137-
Mode: 0644,
141+
Mode: 0o644,
138142
Size: int64(len(data)),
139143
}
140144
if err := t.tw.WriteHeader(hdr); err != nil {

img_tool/pkg/load/load.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,12 @@ func storeBlob(ctx context.Context, store containerd.Store, desc ocispec.Descrip
154154
// Check if the blob already exists
155155
info, err := store.Info(ctx, desc.Digest)
156156
if err == nil && info.Digest == desc.Digest && info.Size == desc.Size {
157-
// Blob already exists with correct size, nothing to do
157+
// Blob already exists with correct size, nothing to do.
158+
// We still print a progress writer for consistency.
159+
if err := progress.CompletedWriter(ctx, desc.Size, desc.Digest.Hex()[:12]); err != nil {
160+
return fmt.Errorf("creating completed progress writer: %w", err)
161+
}
162+
158163
return nil
159164
}
160165

@@ -173,7 +178,10 @@ func storeBlob(ctx context.Context, store containerd.Store, desc ocispec.Descrip
173178
}
174179
defer reader.Close()
175180

176-
pw := progress.Writer(desc.Size, desc.Digest.Hex()[:12])
181+
pw, err := progress.Writer(ctx, desc.Size, desc.Digest.Hex()[:12])
182+
if err != nil {
183+
return fmt.Errorf("creating progress bar: %w", err)
184+
}
177185
if _, err := io.Copy(io.MultiWriter(bufferedWriter, pw), reader); err != nil {
178186
return fmt.Errorf("copying data to writer: %w", err)
179187
}

img_tool/pkg/load/loader.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/bazel-contrib/rules_img/img_tool/pkg/api"
1717
"github.com/bazel-contrib/rules_img/img_tool/pkg/containerd"
1818
"github.com/bazel-contrib/rules_img/img_tool/pkg/docker"
19+
"github.com/bazel-contrib/rules_img/img_tool/pkg/progress"
1920
)
2021

2122
type builder struct {
@@ -106,6 +107,23 @@ func (l *loader) LoadAll(ctx context.Context, ops []api.IndexedLoadDeployOperati
106107

107108
ctx = containerd.WithLease(ctx, lease)
108109

110+
// Setup progress tracking for concurrent blob uploads
111+
ctx, stopProgress := progress.InitProgress(ctx, "loaded")
112+
defer stopProgress()
113+
114+
// Pre-declare all trackers in deterministic order
115+
blobNames := make([]string, len(blobs))
116+
blobSizes := make([]int64, len(blobs))
117+
for i, blob := range blobs {
118+
digest, _ := blob.layer.Digest()
119+
blobNames[i] = digest.Hex[:12]
120+
blobSizes[i], err = blob.layer.Size()
121+
if err != nil {
122+
return nil, fmt.Errorf("getting size of blob %s: %w", digest.String(), err)
123+
}
124+
}
125+
ctx = progress.DeclareTrackers(ctx, blobNames, blobSizes)
126+
109127
// Load all blobs in parallel...
110128
contentStore := client.ContentStore()
111129
if err := uploadBlobsParallel(ctx, contentStore, blobs, defaultWorkers); err != nil {
@@ -186,6 +204,10 @@ func (l *loader) loadContainerd(ctx context.Context, op api.IndexedLoadDeployOpe
186204
}
187205

188206
func (l *loader) loadViaDocker(ctx context.Context, op api.IndexedLoadDeployOperation) ([]string, error) {
207+
// Setup progress tracking for layer streaming
208+
ctx, stopProgress := progress.InitProgress(ctx, "loaded")
209+
defer stopProgress()
210+
189211
// Create a pipe to stream the tar to docker load
190212
pr, pw := io.Pipe()
191213

@@ -314,6 +336,16 @@ func (l *loader) streamManifestToTar(ctx context.Context, manifestInfo api.Manif
314336
tw.SetTags(normalizedTags)
315337
}
316338

339+
// Pre-declare trackers for all layers in order
340+
layerNames := make([]string, len(manifestInfo.LayerBlobs))
341+
layerSizes := make([]int64, len(manifestInfo.LayerBlobs))
342+
for i, layerDesc := range manifestInfo.LayerBlobs {
343+
digest, _ := registryv1.NewHash(layerDesc.Digest)
344+
layerNames[i] = digest.Hex[:12]
345+
layerSizes[i] = layerDesc.Size
346+
}
347+
ctx = progress.DeclareTrackers(ctx, layerNames, layerSizes)
348+
317349
// Stream layers
318350
if err := l.streamLayers(ctx, manifestInfo, tw); err != nil {
319351
return nil, fmt.Errorf("streaming layers: %w", err)
@@ -350,7 +382,7 @@ func (l *loader) streamLayers(ctx context.Context, manifestInfo api.ManifestDepl
350382
}
351383
defer rc.Close()
352384

353-
if err := tw.WriteLayer(digest, layerDesc.Size, rc); err != nil {
385+
if err := tw.WriteLayer(ctx, digest, layerDesc.Size, rc); err != nil {
354386
return err
355387
}
356388
}

img_tool/pkg/progress/BUILD.bazel

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,8 @@ go_library(
55
srcs = ["progress.go"],
66
importpath = "github.com/bazel-contrib/rules_img/img_tool/pkg/progress",
77
visibility = ["//visibility:public"],
8-
deps = ["@com_github_schollz_progressbar_v3//:progressbar"],
8+
deps = [
9+
"@com_github_jedib0t_go_pretty_v6//progress",
10+
"@org_golang_x_term//:term",
11+
],
912
)

0 commit comments

Comments
 (0)