Skip to content
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

libmage: Exists: catch corrupted images #612

Merged
merged 1 commit into from Jun 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
77 changes: 77 additions & 0 deletions libimage/corrupted_test.go
@@ -0,0 +1,77 @@
package libimage

import (
"context"
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/containers/common/pkg/config"
"github.com/containers/storage"
"github.com/containers/storage/pkg/ioutils"
"github.com/stretchr/testify/require"
)

func TestCorruptedImage(t *testing.T) {
// Regression tests for https://bugzilla.redhat.com/show_bug.cgi?id=1966872.
runtime, cleanup := testNewRuntime(t)
defer cleanup()
ctx := context.Background()
pullOptions := &PullOptions{}
pullOptions.Writer = os.Stdout

imageName := "quay.io/libpod/alpine_nginx:latest"

pulledImages, err := runtime.Pull(ctx, imageName, config.PullPolicyAlways, pullOptions)
require.NoError(t, err)
require.Len(t, pulledImages, 1)
image := pulledImages[0]

// Inpsecting a healthy image should work.
_, err = image.Inspect(ctx, false)
require.NoError(t, err, "inspecting healthy image should work")

exists, err := runtime.Exists(imageName)
require.NoError(t, err, "healthy image exists")
require.True(t, exists, "healthy image exists")

// Now remove one layer from the layers.json index in the storage. The
// image will still be listed in the container storage but attempting
// to use it will yield "layer not known" errors.
indexPath := filepath.Join(runtime.store.GraphRoot(), "vfs-layers/layers.json")
data, err := ioutil.ReadFile(indexPath)
require.NoError(t, err, "loading layers.json")
layers := []*storage.Layer{}
err = json.Unmarshal(data, &layers)
require.NoError(t, err, "unmarshaling layers.json")
require.LessOrEqual(t, 1, len(layers), "at least one layer must be present")

// Now write back the layers without the first layer!
data, err = json.Marshal(layers[1:])
require.NoError(t, err, "unmarshaling layers.json")
err = ioutils.AtomicWriteFile(indexPath, data, 0600) // nolint
require.NoError(t, err, "writing back layers.json")

image.reload() // clear the cached data

// Now inspecting the image must fail!
_, err = image.Inspect(ctx, false)
require.Error(t, err, "inspecting corrupted image should fail")

exists, err = runtime.Exists(imageName)
require.NoError(t, err, "corrupted image exists should not fail")
require.False(t, exists, "corrupted image should not be marked to exist")

// Now make sure that pull will detect the corrupted image and repulls
// if needed which will repair the data corruption.
pulledImages, err = runtime.Pull(ctx, imageName, config.PullPolicyNewer, pullOptions)
require.NoError(t, err)
require.Len(t, pulledImages, 1)
image = pulledImages[0]

// Inpsecting a repaired image should work.
_, err = image.Inspect(ctx, false)
require.NoError(t, err, "inspecting repaired image should work")
}
11 changes: 8 additions & 3 deletions libimage/runtime.go
Expand Up @@ -132,13 +132,18 @@ func (r *Runtime) storageToImage(storageImage *storage.Image, ref types.ImageRef
}

// Exists returns true if the specicifed image exists in the local containers
// storage.
// storage. Note that it may return false if an image corrupted.
func (r *Runtime) Exists(name string) (bool, error) {
image, _, err := r.LookupImage(name, &LookupImageOptions{IgnorePlatform: true})
if err != nil && errors.Cause(err) != storage.ErrImageUnknown {
if image == nil || err != nil && errors.Cause(err) != storage.ErrImageUnknown {
return false, err
}
return image != nil, nil
// Inspect the image to make sure if it's corrupted or not.
if _, err := image.Inspect(context.Background(), false); err != nil {
logrus.Errorf("Image %s exists in local storage but may be corrupted: %v", name, err)
return false, nil
}
return true, nil
}

// LookupImageOptions allow for customizing local image lookups.
Expand Down