Skip to content

Commit

Permalink
Report the size of the image that will be downloaded from a registry
Browse files Browse the repository at this point in the history
This uses 'skopeo inspect' to get the size of the image on the registry,
which is usually less than the size of the image in a local
containers/storage image store after download (eg., 'podman images'),
because they are kept compressed on the registry.  Skopeo >= 1.10.0 is
needed to retrieve the sizes [1].

However, this doesn't add a hard dependency on Skopeo to accommodate
size-constrained operating systems like Fedora CoreOS.  If skopeo(1) is
missing or too old, then the size of the image won't be shown, but
everything else would continue to work as before.

Some changes by Debarshi Ray.

[1] Skopeo commit d9dfc44888ff71a6
    containers/skopeo@d9dfc44888ff71a6
    containers/skopeo#641

containers#752

Signed-off-by: Nieves Montero <[email protected]>
  • Loading branch information
nievesmontero authored and debarshiray committed Mar 14, 2023
1 parent 2129e28 commit a1c3095
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 7 deletions.
4 changes: 0 additions & 4 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,6 @@ if shellcheck.found()
test('shellcheck toolbox (deprecated)', shellcheck, args: [toolbox_sh])
endif

if not skopeo.found()
message('Running system tests requires Skopeo for OCI image manipulation.')
endif

install_subdir(
'test',
install_dir: get_option('datadir') / meson.project_name(),
Expand Down
2 changes: 1 addition & 1 deletion playbooks/dependencies-centos-9-stream.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
chdir: '{{ zuul.project.src_dir }}'

- name: Check versions of crucial packages
command: rpm -qa ShellCheck codespell *kernel* gcc *glibc* golang shadow-utils-subid-devel podman conmon containernetworking-plugins containers-common container-selinux crun fuse-overlayfs flatpak-session-helper
command: rpm -qa ShellCheck codespell *kernel* gcc *glibc* golang shadow-utils-subid-devel podman conmon containernetworking-plugins containers-common container-selinux crun fuse-overlayfs flatpak-session-helper skopeo

- name: Show podman versions
command: podman version
Expand Down
8 changes: 8 additions & 0 deletions playbooks/dependencies-fedora-restricted.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@
state: absent
update_cache: "{{ true if zuul.attempts > 1 else false }}"

- name: Ensure that skopeo(1) is absent
become: yes
package:
name:
- skopeo
state: absent
update_cache: "{{ true if zuul.attempts > 1 else false }}"

- name: Download Go modules
shell: |
go mod download -x
Expand Down
2 changes: 1 addition & 1 deletion playbooks/dependencies-fedora.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
chdir: '{{ zuul.project.src_dir }}'

- name: Check versions of crucial packages
command: rpm -qa ShellCheck codespell *kernel* gcc *glibc* shadow-utils-subid-devel golang podman conmon containernetworking-plugins containers-common container-selinux crun fuse-overlayfs flatpak-session-helper
command: rpm -qa ShellCheck codespell *kernel* gcc *glibc* shadow-utils-subid-devel golang podman conmon containernetworking-plugins containers-common container-selinux crun fuse-overlayfs flatpak-session-helper skopeo

- name: Show podman versions
command: podman version
Expand Down
36 changes: 35 additions & 1 deletion src/cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import (
"github.com/briandowns/spinner"
"github.com/containers/toolbox/pkg/podman"
"github.com/containers/toolbox/pkg/shell"
"github.com/containers/toolbox/pkg/skopeo"
"github.com/containers/toolbox/pkg/utils"
"github.com/docker/go-units"
"github.com/godbus/dbus/v5"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -573,6 +575,30 @@ func getFullyQualifiedImageFromRepoTags(image string) (string, error) {
return imageFull, nil
}

func getImageSizeFromRegistry(imageFull string) (string, error) {
image, err := skopeo.Inspect(imageFull)
if err != nil {
return "", err
}

if image.LayersData == nil {
return "", errors.New("'skopeo inspect' did not have LayersData")
}

var imageSizeFloat float64

for _, layer := range image.LayersData {
if layerSize, err := layer.Size.Float64(); err != nil {
return "", err
} else {
imageSizeFloat += layerSize
}
}

imageSizeHuman := units.HumanSize(imageSizeFloat)
return imageSizeHuman, nil
}

func getServiceSocket(serviceName string, unitName string) (string, error) {
logrus.Debugf("Resolving path to the %s socket", serviceName)

Expand Down Expand Up @@ -687,7 +713,15 @@ func pullImage(image, release, authFile string) (bool, error) {
if promptForDownload {
fmt.Println("Image required to create toolbox container.")

prompt := fmt.Sprintf("Download %s (500MB)? [y/N]:", imageFull)
var prompt string

if imageSize, err := getImageSizeFromRegistry(imageFull); err != nil {
logrus.Debugf("Getting image size failed: %s", err)
prompt = fmt.Sprintf("Download %s? [y/N]:", imageFull)
} else {
prompt = fmt.Sprintf("Download %s (%s)? [y/N]:", imageFull, imageSize)
}

shouldPullImage = askForConfirmation(prompt)
}

Expand Down
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ sources = files(
'cmd/utils.go',
'pkg/podman/podman.go',
'pkg/shell/shell.go',
'pkg/skopeo/skopeo.go',
'pkg/utils/libsubid-wrappers.c',
'pkg/utils/errors.go',
'pkg/utils/utils.go',
Expand Down
50 changes: 50 additions & 0 deletions src/pkg/skopeo/skopeo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright © 2023 Red Hat Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package skopeo

import (
"bytes"
"encoding/json"

"github.com/containers/toolbox/pkg/shell"
)

type Layer struct {
Size json.Number
}
type Image struct {
LayersData []Layer
}

func Inspect(target string) (*Image, error) {
var stdout bytes.Buffer

targetWithTransport := "docker://" + target
args := []string{"inspect", "--format", "json", targetWithTransport}

if err := shell.Run("skopeo", nil, &stdout, nil, args...); err != nil {
return nil, err
}

output := stdout.Bytes()
var image Image
if err := json.Unmarshal(output, &image); err != nil {
return nil, err
}

return &image, nil
}

0 comments on commit a1c3095

Please sign in to comment.