The imo module manages incremental updates for container images in
registries. It enables users to pull differences between image versions and
push these "incremental updates" to a registry, which can significantly reduce
data transfer size, particularly beneficial in air-gapped environments.
The Incremental type provides methods to retrieve (using Pull) or transmit
(using Push) the differences between two images. These differences are
determined based on the base and final images.
When comparing an image tag v1 with an image tag v2, the base image is
v1, and the final image is v2. The Incremental object calculates the
difference between these two images.
- New
- Creates a new Incremental object, enabling callers to calculate or send the incremental difference between two images.
- Pull
- Pulls the incremental difference between two images as an
io.ReadCloser, from which a tarball can be read. The caller is responsible for closing the reader.
- Pulls the incremental difference between two images as an
- PushVet
- Verifies whether all necessary layers exist in the destination registry. Returns an error if any layer is missing. Particularly useful before pushing an incremental update.
- Push
- Pushes the incremental difference stored in a tarball to the destination registry. Fails if the remote registry lacks any required layers not included in the incremental update.
The imo library allows you to pull differences between container images as a
tarball:
package main
import (
"context"
"io"
"os"
"github.com/ricardomaraschini/imo"
)
func pull() {
// create a new incremental puller
inc := imo.New(
imo.WithReporterWriter(os.Stdout),
imo.WithBaseAuth("user", "pass"),
imo.WithFinalAuth("user2", "pass2"),
)
// pull the differential update
diff, err := inc.Pull(
context.Background(),
"docker.io/myaccount/myapp:v1.0.0",
"docker.io/myaccount/myapp:v2.0.0",
)
if err != nil {
panic(err)
}
defer diff.Close()
// save the differential update to a file
fp, err := os.Create("difference.tar")
if err != nil {
panic(err)
}
defer fp.Close()
if _, err := io.Copy(fp, diff); err != nil {
panic(err)
}
}The method Pull requires two image tags: the base image and the final image.
If you want to pull an image as a whole, without calculating the difference, you
can use scratch as base:
// this will pull docker.io/myaccount/myapp:v1.0.0.
diff, err := inc.Pull(
context.Background(),
"scratch",
"docker.io/myaccount/myapp:v1.0.0",
)You can also use imo to push a differential update to a container registry.
Before pushing it is important to validate that the remote registry has all the
layers not included in the incremental difference. You can use PushVet for
this:
package main
import (
"context"
"os"
"github.com/ricardomaraschini/imo"
)
func push() {
// create a new incremental pusher
inc := imo.New(
imo.WithReporterWriter(os.Stdout),
imo.WithPushAuth("user", "pass"),
)
// check if the remote registry has all needed layers.
if err := imo.PushVet(
context.Background(),
"difference.tar",
"myregistry.io/myaccount/app:v1.0.0",
); err != nil {
panic(err)
}
// push the differential update to the registry
if err := inc.Push(
context.Background(),
"difference.tar",
"myregistry.io/myaccount/app:v2.0.0",,
); err != nil {
panic(err)
}
}